This is not copy-initializing, or is it?
Asked Answered
W

1

6

In the following code I am not allowed to declare an explicit ctor because the compiler says I am using it in a copy-initializing context (clang 3.3 and gcc 4.8). I try to prove the compilers wrong by making the ctor non explicit and then declaring the copy constructors as deleted.

Are the compilers wrong or is there any other explanation?

#include <iostream>

template <typename T>
struct xyz
{
    constexpr xyz (xyz const &)    = delete;
    constexpr xyz (xyz &&)         = delete;
    xyz & operator = (xyz const &) = delete;
    xyz & operator = (xyz &&)      = delete;
    T i;
    /*explicit*/ constexpr xyz (T i): i(i) { }
};

template <typename T>
xyz<T> make_xyz (T && i)
{
    return {std::forward<T>(i)};
}

int main ()
{
    //auto && x = make_xyz(7);
    auto && x (make_xyz(7)); // compiler sees copy-initialization here too
    std::cout << x.i << std::endl;
}

Update An unrealistic but much simpler version

struct xyz {
    constexpr xyz (xyz const &) = delete;
    constexpr xyz (xyz &&) = delete;
    xyz & operator = (xyz const &) = delete;
    xyz & operator = (xyz &&) = delete;
    int i;
    explicit constexpr xyz (int i): i(i) { }
};

xyz make_xyz (int && i) {
    return {i};
}

int main () {
    xyz && x = make_xyz(7); 
}
Weakly answered 13/12, 2013 at 5:18 Comment(0)
T
7

The = notation should not affect the complaint because reference binding doesn't behave differently whether expressed by direct- or copy-initialization. What's being initialized here is the return value object, which does not have its own name.

Unfortunately, GCC is right to complain, as does Clang. According to §6.6.3/2 [stmt.return],

A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list.

So, there is an invisible = sign there and you can't get around it.

Tenenbaum answered 13/12, 2013 at 5:28 Comment(5)
What a shame! First they tell us to be clean and use explicit where it makes sense. Then they hammer our fingers when we do 8-(.Weakly
@PatrickFromberg TBH I'm not sure where explicit makes sense, aside from the C++03 case, or where a multi-argument call could be ambiguous with a std::initializer_list overload. But for no arguments, the default constructor has specially higher precedence over std::initializer_list anyway. The nominal effect of explicit is to force you to name the class rather than allow it to be implicit, so in this case it did its job :P .Tenenbaum
Regarding "force you to name the class". There is actually no copy between the initializer-list and the named result type. I have added a simpler example to my question to make sure. After all the ultimate reason for copy-initializer and explicit incompatibility is to prevent obscure conversions. But I see no obscure conversion here. Unfortunately as you cite correctly, the standard says this is copy initialization by definition and you can not argue against definition.Weakly
@PatrickFromberg The term copy-list-initialization does not refer to the copy constructor but only equivalence to usage of the = sign. It is not "copy initialization" but specifically copy-list-initialization which is an indivisible, somewhat confusingly named, concept.Tenenbaum
"equivalence to usage of = sign" does not really reduce the confusion. But it is not your fault, I saw that description in the C++ Bible too. But I missed indeed the difference between copy-initialization and copy-list-initialization. Good that you point out that.Weakly

© 2022 - 2024 — McMap. All rights reserved.