3

It seems quite weird. Here you can see the error message is that a convert happens between one type and it fails. If I remove the explicit modifier from Vector3's copy constructor it is fine, no error. Could someone explain why? I'm confused.

template<typename T>
class Vector3 {
public:
    explicit Vector3(const Vector3& v) :x(v.x), y(v.y), z(v.z) {}
    Vector3() :x(0), y(0), z(0) {}
    T x, y, z;
};
template<typename T>
Vector3<T> getVec3() {
    return Vector3<T>(); //c2440 "return":cannot convert Vector3<int> to Vector3<int>
}
int main()
{   
    getVec3<int>();
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Copy constructor should not be declared `explicit`. – Oasin Jul 06 '22 at 02:39
  • It compiles using `/std:c++17` or later, fails with `/std:c++14`. https://godbolt.org/z/MPh9e6vz4 – Retired Ninja Jul 06 '22 at 02:43
  • `getVec3` is performing [RVO](https://en.cppreference.com/w/cpp/language/copy_elision), where move ctor is *meant to be* called implicitly. But when you define a copy ctor yet lack a move ctor, you are suppressing the implicitly defined default move ctor. In your case, you should define neither, as your class does not hold any ownership. See [the rule of zero](https://en.cppreference.com/w/cpp/language/rule_of_three). – Oasin Jul 06 '22 at 02:48

1 Answers1

5

return Vector3<T>(); performs copy initialization, which won't consider explicit constructors: including the copy constructor. That's why you should mark the copy constructor non-explicit.

Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.

BTW: Since C++17 your code would work fine because of mandatory copy elision, the copy (and move construction) will be omitted completely.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405