Шаблонные функции: конструкция по умолчанию без копирования в C++

Учитывая

struct C { 
   C()            { printf("C::C()\n"          ); }
   C(int)         { printf("C::C(int)\n"       ); }
   C( const C& )  { printf("copy-constructed\n"); }
};

И шаблонная функция

template< typename T > void foo(){
    // default-construct a temporary variable of type T
    // this is what the question is about.
    T t1;       // will be uninitialized for e.g. int, float, ...
    T t2 = T(); // will call default constructor, then copy constructor... :(
    T t3();     // deception: this is a local function declaration :(
}

int main(){
    foo<int>();
    foo<C  >();
}

Глядя на t1, он не будет инициализирован, когда T, например. int. С другой стороны, t2 будет скопировано из созданного по умолчанию временного объекта.

Вопрос: возможно ли в С++ создать общую переменную по умолчанию, кроме как с шаблоном-фу?


person xtofl    schedule 14.03.2011    source источник
comment
для тех, кто думает... Я видел такой вопрос раньше, сегодня... Они правы. Но именно эта сторона вопроса осталась без ответа.   -  person xtofl    schedule 14.03.2011
comment
@xtofl: что такое template-fu?   -  person Nawaz    schedule 14.03.2011
comment
@Nawaz: C++ версия кунг-фу.   -  person Ben Voigt    schedule 14.03.2011
comment
@Бен: Круто. Достаточно ли популярен этот термин в сообществе C++? Или недавно изобретенный?   -  person Nawaz    schedule 14.03.2011
comment
@Nawaz: [все]-фу популярно. В основном встречается в таких предложениях, как «Мой питон-фу недостаточно силен».   -  person Calvin1602    schedule 14.03.2011
comment
@Nawaz: постфикс «фу» часто используется для обозначения «высокого мастерства». По аналогии с часто используемым «скрипт-фу». Извините, что ввел вас в заблуждение.   -  person xtofl    schedule 14.03.2011


Ответы (4)


Вот трюк, который вы можете использовать, используя локальный класс:

template <typename T> void foo() {
    struct THolder {
        T obj;
        THolder() : obj() { } // value-initialize obj
    };

    THolder t1; // t1.obj is value-initialized
}

Кажется, я читал об этом трюке из ответа на другой вопрос о переполнении стека, но в данный момент я не могу найти этот вопрос.

В качестве альтернативы вы можете использовать шаблон класса boost::value_initialized<T>, который в основном делает то же самое, но с большей гибкостью и согласованностью, а также с обходными путями для компиляторов с ошибками.

В C++0x это намного проще: вы можете использовать пустой список инициализаторов:

T obj{}; // obj is value-initialized

(Насколько мне известно, только gcc 4.5+ поддерживает списки инициализаторов C++0x. Clang и Visual C++ еще не поддерживают их.)

person James McNellis    schedule 14.03.2011
comment
Я думаю, вы могли бы прочитать это из моего ответа на указанный вопрос :) «тип возвращаемого значения шаблона со значением по умолчанию»> stackoverflow.com/questions/5300602/ - person xtofl; 14.03.2011
comment
@xtofl: А, так ты уже знаешь об этом трюке. Насколько мне известно, это способ С++ 03 (в С++ 0x вы можете использовать список инициализаторов). [Я не видел этот вопрос сегодня утром; этот трюк упоминался где-то в прошлом году] - person James McNellis; 14.03.2011
comment
Я ждал, что кто-нибудь упомянет C++0x. Не могли бы вы привести пример для этого? - person xtofl; 14.03.2011
comment
@xtofl: я добавил, как это можно сделать с пустым списком инициализаторов C++0x. - person James McNellis; 14.03.2011
comment
@xtofl: в C++0x это должно быть просто X x{};. Однако кажется, что в настоящее время некоторые компиляторы могут (очевидно, ошибочно?!) вызывать перегрузку конструктора, принимающую initializer_list, если она доступна (см. ideone.com/Il93E и закомментируйте перегрузку для сравнения). - person UncleBens; 14.03.2011
comment
Спасибо. Это как бы отвечает на мой вопрос: нет, в C++‹0x такого пути нет, но C++0x предусматривает в «пустом» списке инициализаторов. - person xtofl; 14.03.2011
comment
C++‹0x выглядит как большеголовый четырехрукий чувак на скейтборде или что-то в этом роде. ;-) - person James McNellis; 14.03.2011
comment
@UncleBens, да, ошибочно. См. open-std.org/jtc1/sc22/wg21. /docs/cwg_defects.html#990 - person Johannes Schaub - litb; 15.03.2011

Если вас не волнует тот факт, что конструктор копирования должен существовать, и вы просто хотите предотвратить его вызов:

Не волнуйтесь: этого не будет. В этой ситуации вызов конструктора копирования будет опущен. Всегда и надежно — даже при компиляции с отключенной оптимизацией (-O0).

person Konrad Rudolph    schedule 14.03.2011
comment
Всегда и надежно — на каждом когда-либо созданном компиляторе C++, и даже когда конструктор копирования недоступен? - person Ben Voigt; 14.03.2011
comment
@Ben К первой части: обо всем этом. Ко второй части: я обратился к этому в первом предложении моего ответа. - person Konrad Rudolph; 14.03.2011
comment
Всегда и надежно — это слишком громкое заявление! - person Nawaz; 14.03.2011
comment
@Nawaz Этот код является фиксированной идиомой, и в стандарте прямо упоминается, что компиляторы могут это оптимизировать. Я достаточно уверен, что даже не столь свежие компиляторы поступают правильно. Тем не менее, я говорю о реальном мире. Конечно, любой может создать соответствующий компилятор только для того, чтобы нарушить это единственное утверждение. Но это не совсем актуально. - person Konrad Rudolph; 14.03.2011
comment
Хотя компилятор удаляет ненужные копии, он все равно требует наличия конструктора копирования. - person Maxim Egorushkin; 14.03.2011
comment
Даже Visual C++ 6 делает это, даже в режиме отладки. Однако больше не могу найти источник для этого. - person Calvin1602; 14.03.2011
comment
@Konrad: небольшая вариация, и конструктор-копия не был исключен (что вызвало всевозможные огорчения): stackoverflow.com/questions/5305391/ - person Ben Voigt; 15.03.2011
comment
@Ben Извините, но это совершенно другой случай. Этот вопрос был конкретно о построении шаблонного значения по умолчанию, а не об исключении копии в возвращаемых значениях. T x = T() — распространенный и известный вариант использования, потому что часто необходимо предотвратить самый неприятный анализ T x() (и тому подобного). Каждый производитель компилятора знает, что этого можно ожидать. - person Konrad Rudolph; 16.03.2011
comment
@Konrad: Мне кажется, что этот вопрос касается исключения копии возвращаемого значения из конструктора по умолчанию. Правда, другой пример не является конструктором по умолчанию, но в остальном он похож. - person Ben Voigt; 16.03.2011

Каков ваш настоящий вопрос? Конструктор по умолчанию вызывается для экземпляра t1.

person florin    schedule 14.03.2011
comment
Да, если T является типом класса. Если T равно, например, int, то t1 останется неинициализированным. - person James McNellis; 14.03.2011
comment
ну... это не для int. Попробую переформулировать вопрос. - person xtofl; 14.03.2011
comment
@James: но в чем смысл упражнения? Это пустяковый вопрос? Пытается ли он автоматически инициализировать переменные? - person florin; 14.03.2011
comment
Он пытается написать универсальный код, который работает как для классов, так и для встроенных типов, и не требует дополнительных затрат ни для того, ни для другого. - person xtofl; 14.03.2011

Т т2 = Т(); // вызовет конструктор по умолчанию, затем скопирует конструктор... :(

Не на моем компиляторе (VC2008). Выход для меня...

C::C()
C::C()

Именно этого я и ожидал. Я что-то упускаю?

person Martin Stone    schedule 14.03.2011
comment
Это потому, что вызов конструктора копирования можно опустить. Если конструктор копирования был недоступен или иным образом не вызывался, то T t2 = T(); не будет работать. - person James McNellis; 14.03.2011
comment
Хорошо, теперь я понял. Возможно, комментарий в вопросе нужно читать // Требуется доступный конструктор копирования :( - person Martin Stone; 14.03.2011