Am Mittwoch, 19. Juli 2017 19:00:05 UTC+2 schrieb Stefan Reuther:
>
> template<typename T>
> class foo {
> public:
>
> foo(const foo& f) //1
>
> template<typename U>
> foo(U&& u) //2
>
> };
>
> void bar() {
> foo<std::string> a;
> foo<std::string> b(a); //3
> }
> ----8<--------8<--------8<--------8<--------8<----
>
> In Zeile //3 wählt der Compiler (z.B. g++-5.4.0 -std=c++11) den Overload
> //2 und beschwert sich dann, dass er das keinen passenden
> Zuweisungsoperator für 'm = u' findet.
>
> Erwartet hätte ich, dass Overload //1 gewählt wird. Wenn ich //2
> auskommentiere, ist das auch der Fall.
>
> Gewohnt bin ich aus C++98, dass der Compiler gerade fürs Kopieren eines
> Objektes einen Template-Konstruktor nicht einmal zur Kenntnis nimmt.
Das war aber noch nie so.
> Wie bekomme ich das aufgelöst?
Wegen //3 wird bei //2 der Parameter U mit foo<string>& deduziert und
ist ein besserer Match als //1, weil da bei //1 noch ein const dazu
kommen würde.
Eine Möglichkeit wäre, dem zweiten Konstruktor quasi einen Namen
geben. Das findest Du so so ähnlich auch bei std::pair. Das Ding hat
nämlich folgenden Konstruktor ab C++11:
template< class... Args1, class... Args2 >
pair( std::piecewise_construct_t,
std::tuple<Args1...> first_args,
std::tuple<Args2...> second_args );
Aufrufen tut man den dann so:
pair<T,U> x (std::piecewise_construct,
std::forward_as_tuple(...),
std::forward_as_tuple(...) );
Der erste "dummy" Parameter ist quasi der Name des Konstruktors.
Du kannst aber auch sowas schreiben, wenn Du magst:
template<typename U
,class = typename std::enable_if<
std::is_constructible<T,U&&>::value
>::type
>
foo(U&& u)
: m(std::forward<U>(u))
{}
(Bei dem type-trait bin ich mir gerade nicht sicher. Habe ich
jetzt nicht extra nachgeguckt)