M>У меня вопрос с коллегам , зачем такой конструктор мог понадобиться ? M>Спасибо!
Это не конструктор копирования, это обычный конструктор, который позволяет создать объект типа FTypeName, используя инициализатор любого подходящего типа.
M>>У меня вопрос с коллегам , зачем такой конструктор мог понадобиться ? M>>Спасибо!
B>Это не конструктор копирования, это обычный конструктор, который позволяет создать объект типа FTypeName, используя инициализатор любого подходящего типа.
А чем он отличается от:
template <typename T>
class FTypeName // STACK-BASED
{
T m_val;
T m_diff[N];
bool m_depend;
public:
FTypeName(const typename T& val):m_val(val),m_depend(false)
{
}
};
M>У меня вопрос с коллегам , зачем такой конструктор мог понадобиться ? M>Спасибо!
как вариант — решили сэкономить на создании копии (копия будет создана только один раз в m_val(val), а если написать конструктор от Т, то будет две копии, хотя я на 90% уверен, что эта лишняя копия будет изничтожена компилятором).
Jazzer ниже привел одно отличие.
Еще можно предположить, что авторы хотели, чтобы преобразование U -> T выполнялось "внутри" класса FTypeName. Вот пример для иллюстрации:
template <class T>
class Test2
{
T t_;
public:
template<class U>
Test2(const U& u) : t_(u) {}
};
class Test
{
template <class T> friend class Test2;
private:
operator int() const { return 1; }
};
int main()
{
Test t;
Test2<double> t2(t);
}
Здравствуйте, jazzer, Вы писали:
J>как вариант — решили сэкономить на создании копии (копия будет создана только один раз в m_val(val), а если написать конструктор от Т, то будет две копии, хотя я на 90% уверен, что эта лишняя копия будет изничтожена компилятором).
Только вот 90% уверенности — это многовато аналогично insert(make_pair/value_type):
Кстати, к вопросу о сабже — этот конструктор не является конструктором копирования, если для этого класса компилятор способен сгенерить такой конструктор (а судя по описанию, он способен это сделать).
т.е. когда компилятор сгенерит копиктор, в классе получится вот что:
Тогда при копировании получится U = FTypeName, т.е. полное совпадение сигнатуры, при которой выигрывает нешаблонная функция.
Т.е. если это было попыткой убить одним выстрелом двух зайцев (конструктор из другого типа и копиктор), то это не сработает, будет вызвано то, что молча сгенерил компилятор.
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, jazzer, Вы писали:
J>Кстати, к вопросу о сабже — этот конструктор не является конструктором копирования, если для этого класса компилятор способен сгенерить такой конструктор (а судя по описанию, он способен это сделать). J>т.е. когда компилятор сгенерит копиктор, в классе получится вот что: J>
J>Тогда при копировании получится U = FTypeName, т.е. полное совпадение сигнатуры, при которой выигрывает нешаблонная функция.
Это справедливо для функций, в случае с конструкторами поведение другое:
12.8/3
A member function template is never instantiated to perform the copy of a class object to an object of its class type.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
J>>Тогда при копировании получится U = FTypeName, т.е. полное совпадение сигнатуры, при которой выигрывает нешаблонная функция. ЮЖ>Это справедливо для функций, в случае с конструкторами поведение другое: ЮЖ>
12.8/3
ЮЖ>A member function template is never instantiated to perform the copy of a class object to an object of its class type.
Имхо, это относится только к случаю X(X), а не к X(const X&), просто потому что конструктор вида X(X) запрещен из-за бесконечной рекурсии и попытка инстанцирования немедленно привела бы к ошибке, а к конструторам из ссылки это не относится, тут достаточно простого правила про преимущество нешаблонной функции.
Здравствуйте, sokel, Вы писали:
S>на каком компиляторе/уровне оптимизации будет выкинуто создание копии в конструкторе holder_1?
у меня есть только гцц 3.4.6, он не выкидывает.
Но я не вижу причин, по которым бы временный объект, который там создается при вызове конструктора holder_1, не мог бы быть выкинут и s не могла бы быть инициализирована напрямую, учитывая то, что весь код компилятору доступен.
Можно, разве что, порассуждать про ОДР, но, имхо, ОДР применима только к коду, который написан программером, а не к оптимизированному коду, который генерится компилятором каждый раз по-своему, в зависимости от локальной ситуации...
12.8/3
A member function template is never instantiated to perform the copy of a class object to an object of its class type.
IMHO, это очередной дефект стандарта. Из 8.5/14/4/2, 13.3.1.3 и правил разрешения перегрузки следует, что при инициализации объекта классового типа выражением такого же типа шаблонный конструктор в некоторых случаях может иметь преимущество перед копирующим конструктором. Например, такая программа
#include <iostream>
struct X
{
X() {}
template <class T>
X(T &)
{
std::cout << "template ctor" << std::endl;
}
private:
X(X const &);
};
struct Y
{
Y() {}
template <class T>
Y(T const &)
{
std::cout << "template ctor" << std::endl;
}
private:
explicit Y(Y const &);
};
int main()
{
X x;
Y y;
X x1 = x;
X x2(x);
Y y1 = y;
}
должна вывести
template ctor
template ctor
template ctor
Случай, когда могла бы возникнуть бесконечная рекурсия, было бы уместно описать в 14-ом разделе как одну из причин неудачной дедукции типа (такая специализация автоматически исключалась бы из множества candidate functions), а в 12.8/3 нужно уточнить вид первого параметра шаблонного конструктора. А то получается непонятно что.
jazzer:
S>>на каком компиляторе/уровне оптимизации будет выкинуто создание копии в конструкторе holder_1?
J>у меня есть только гцц 3.4.6, он не выкидывает. J>Но я не вижу причин, по которым бы временный объект, который там создается при вызове конструктора holder_1, не мог бы быть выкинут и s не могла бы быть инициализирована напрямую, учитывая то, что весь код компилятору доступен.
Данный случай не подпадает под условия 12.8/15, поэтому исполнение действий, осуществляемых в копирующем конструкторе, может быть устранено только при условии, что observable behavior будет таким же, как если бы ничего не устранялось, и компилятор умеет это обнаруживать. (В случаях же, подпадающих под условия 12.8/15, устранение вызова копирующего конструктора может на вполне законных основаниях менять observable behavior — следовательно, такой вид оптимизации производить гораздо проще)
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Юрий Жмеренецкий, Вы писали:
J>>>Тогда при копировании получится U = FTypeName, т.е. полное совпадение сигнатуры, при которой выигрывает нешаблонная функция. ЮЖ>>Это справедливо для функций, в случае с конструкторами поведение другое: ЮЖ>>
12.8/3
ЮЖ>>A member function template is never instantiated to perform the copy of a class object to an object of its class type.
J>Имхо, это относится только к случаю X(X), а не к X(const X&), просто потому что конструктор вида X(X) запрещен из-за бесконечной рекурсии и попытка инстанцирования немедленно привела бы к ошибке, а к конструторам из ссылки это не относится, тут достаточно простого правила про преимущество нешаблонной функции.
Недостаточно. Т.е. недостаточно если рассматривать преимущество нешаблонной функции как единственный фактор, на основании которого происходит выбор:
struct A
{
A();
A(A&);
template<class T>
A(const T&)
{}
};
struct B : A
{
B();
};
int main()
{
B b;
B b1 = b; // error
}
Здравствуйте, Николай Ивченков, Вы писали:
НИ>Юрий Жмеренецкий:
ЮЖ>>
12.8/3
НИ>A member function template is never instantiated to perform the copy of a class object to an object of its class type.
НИ>IMHO, это очередной дефект стандарта. Из 8.5/14/4/2, 13.3.1.3 и правил разрешения перегрузки следует, что при инициализации объекта классового типа выражением такого же типа шаблонный конструктор в некоторых случаях может иметь преимущество перед копирующим конструктором.
Но ведь это инициализация, а не копирование... При copy-initialization рассматриваются все converting constructors, в число которых входит и non-explicit copy constructor (12.3.1/3). При direct — те же, включая explicit версии. Какой конкретно конструктор будет выбран — зависит от ситуации.
В местах где может быть использован только copy constructor — будет использоваться именно он.
НИ>Случай, когда могла бы возникнуть бесконечная рекурсия, было бы уместно описать в 14-ом разделе как одну из причин неудачной дедукции типа. НИ>а в 12.8/3 нужно уточнить вид первого параметра шаблонного конструктора. А то получается непонятно что.
Почему не понятно что? Первое предложение не относится к function template. Даже если не принимать во внимание это предложение, все равно я не вижу способа вызвать рекурсию с использованим template converting constructor'а. Попытки сделать это намеренно приведут к UB согласно 14.7.1/14, только к самому конструктору это никак не относится.
Здравствуйте, Николай Ивченков, Вы писали:
НИ>jazzer:
S>>>на каком компиляторе/уровне оптимизации будет выкинуто создание копии в конструкторе holder_1?
J>>у меня есть только гцц 3.4.6, он не выкидывает. J>>Но я не вижу причин, по которым бы временный объект, который там создается при вызове конструктора holder_1, не мог бы быть выкинут и s не могла бы быть инициализирована напрямую, учитывая то, что весь код компилятору доступен.
НИ>Данный случай не подпадает под условия 12.8/15, поэтому исполнение действий, осуществляемых в копирующем конструкторе, может быть устранено только при условии, что observable behavior будет таким же, как если бы ничего не устранялось, и компилятор умеет это обнаруживать. (В случаях же, подпадающих под условия 12.8/15, устранение вызова копирующего конструктора может на вполне законных основаниях менять observable behavior — следовательно, такой вид оптимизации производить гораздо проще)
Может быть, может быть, я не смотрел стандарт, времени нету сейчас совсем.
Просто суть этой оптимизации в том, чтоб разрешить избавляться от временных объектов там, где они не нужны.
Имхо, это как раз такой случай (по крайней мере, я навскидку не вижу принципиальных препятствий), но он вполне может быть и не прописан в стандарте явно.
Юрий Жмеренецкий:
ЮЖ>Но ведь это инициализация, а не копирование...
А что такое копирование? В 12.8/1 сказано:
A class object can be copied in two ways, by initialization (12.1, 8.5), including for function argument passing (5.2.2) and for function value return (6.6.3), and by assignment (5.17). Conceptually, these two operations are implemented by a copy constructor (12.1) and copy assignment operator (13.5.3).
но здесь "copied" курсивом не выделено. Надо ли считать этот абзац определением копирования, неясно. Только ли копирующий конструктор может осуществлять копирование в виде инициализации? В сноске 106, например, говорится:
Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.
В отношении неявного определения копирующего конструктора, вроде бы, прямо сказано про использование копирующего конструктора подобъекта (12.8/8):
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Each subobject is copied in the manner appropriate to its type:
— if the subobject is of class type, the copy constructor for the class is used;
— if the subobject is an array, each element is copied, in the manner appropriate to the element type;
— if the subobject is of scalar type, the built-in assignment operator is used.
Однако, следующий пример
#include <iostream>
struct X
{
X() {}
template <class T>
explicit X(T &)
{
std::cout << "template ctor" << std::endl;
}
private:
X(X const volatile &);
};
struct Y
{
X x;
};
int main()
{
Y y1;
Y y2(y1);
}
компилируют VC++, GNU C++ и даже Comeau, т.е. они по сути выполняют почленную direct-initialization.
НИ>>Случай, когда могла бы возникнуть бесконечная рекурсия, было бы уместно описать в 14-ом разделе как одну из причин неудачной дедукции типа. НИ>>а в 12.8/3 нужно уточнить вид первого параметра шаблонного конструктора. А то получается непонятно что. ЮЖ>Почему не понятно что?
Рассмотрим такой пример:
struct X
{
template <class T>
X(T)
{
}
};
int main()
{
volatile X x1(1);
X x2(x1);
}
Как здесь формируется set of candidate functions при инициализации x1? Шаблон функции-конструктора не может быть candidate function, кандидатом может быть специализация шаблона. Чтобы получить специализацию, нужно произвести дедукцию шаблонного аргумента для шаблонного параметра T и затем подставить выведенный тип на место T. Полученная специализация T(int) и будет одной из candidate functions (второй будет копирующий конструктор). А как формируется set of candidate functions при инициализации x2? Где в стандарте можно увидеть внятное объяснение?
ЮЖ>Первое предложение не относится к function template.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здравствуйте, jazzer, Вы писали:
J>>Здравствуйте, Юрий Жмеренецкий, Вы писали:
J>>>>Тогда при копировании получится U = FTypeName, т.е. полное совпадение сигнатуры, при которой выигрывает нешаблонная функция. ЮЖ>>>Это справедливо для функций, в случае с конструкторами поведение другое: ЮЖ>>>
12.8/3
ЮЖ>>>A member function template is never instantiated to perform the copy of a class object to an object of its class type.
J>>Имхо, это относится только к случаю X(X), а не к X(const X&), просто потому что конструктор вида X(X) запрещен из-за бесконечной рекурсии и попытка инстанцирования немедленно привела бы к ошибке, а к конструторам из ссылки это не относится, тут достаточно простого правила про преимущество нешаблонной функции.
ЮЖ>Недостаточно. Т.е. недостаточно если рассматривать преимущество нешаблонной функции как единственный фактор, на основании которого происходит выбор:
пример поскипан, у меня 3 часа ночи, я в такое время думать не могу, завтра подумаю.
Единственное соображение — то, что ты цитируешь, вырвано из абзаца, в котором говорится про конструктор вида Х(Х), посему я настаиваю, что и цитата относится только к этому виду конструктора.
Если твой пример показывает что-то другое, то должно быть какое-то еще объяснение либо это просто дефект стандарта (опять же, в 3 часа ночи я в список дефектов не полезу), но не процитированный текст.
Здравствуйте, Николай Ивченков, Вы писали:
НИ>Юрий Жмеренецкий:
ЮЖ>>Но ведь это инициализация, а не копирование... НИ>А что такое копирование?
Возможное действие выполняемое при инициализации =)
НИ>В 12.8/1 сказано: НИ>
A class object can be copied in two ways, by initialization (12.1, 8.5), including for function argument passing (5.2.2) and for function value return (6.6.3), and by assignment (5.17). Conceptually, these two operations are implemented by a copy constructor (12.1) and copy assignment operator (13.5.3).
НИ>но здесь "copied" курсивом не выделено. Надо ли считать этот абзац определением копирования, неясно. Только ли копирующий конструктор может осуществлять копирование в виде инициализации?
DR#331: Allowed copy constructor signatures:
Change 12.1 [class.ctor] paragraph 10 from
A copy constructor for a class X is a constructor with a first parameter of type X& or of type const X&. [Note: see 12.8 [class.copy] for more information on copy constructors. ]
to :
A copy constructor (12.8 [class.copy]) is used to copy objects of class type.
НИ>В отношении неявного определения копирующего конструктора, вроде бы, прямо сказано про использование копирующего конструктора подобъекта (12.8/8): НИ>
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Each subobject is copied in the manner appropriate to its type:
НИ>— if the subobject is of class type, the copy constructor for the class is used;
В core language active issue #535 "Copy construction without a copy constructor" предлагают заменить эту строку следующей:
if the subobject is of class type, direct-initialization (8.5 [dcl.init]) is performed
Там же предлагается еще 13 подобных исправлений.
ЮЖ>>Почему не понятно что? НИ>Рассмотрим такой пример:
НИ>
struct X
НИ>{
НИ> template <class T>
НИ> X(T)
НИ> {
НИ> }
НИ>};
НИ>int main()
НИ>{
НИ> volatile X x1(1);
НИ> X x2(x1);
НИ>}
НИ>Как здесь формируется set of candidate functions при инициализации x1?
Так же как и при использовании функций. 13.3.1/7:
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way
НИ>А как формируется set of candidate functions при инициализации x2? Где в стандарте можно увидеть внятное объяснение?
Что-то вроде такого:
13.3.1.3/1:
For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized.
Варианты:
1) X(const X&)
2) X(X)
Аргумент — volatile lvalue
13.3.1/2
For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
Итого:
1) volatile X -> const X&
2) volatile X -> X
Оба варианта в конечном итоге не могут быть использованы из-за C.1.8:
The implicitly-declared copy constructor and implicitly-declared copy assignment operator cannot make a copy of a volatile lvalue
Здравствуйте, jazzer, Вы писали:
J>Если твой пример показывает что-то другое, то должно быть какое-то еще объяснение либо это просто дефект стандарта (опять же, в 3 часа ночи я в список дефектов не полезу), но не процитированный текст.
Пример можно считать неактуальным. Некоторые объяснения есть в ответе Николаю.