2

Suppose this is my class:

#include<utility>
#include<type_traits>

template<typename T>
class MyClass {
    T v;
public:
    template<typename...Ts>
    MyClass(Ts&&...args) :v{ std::forward<Ts>(args)... } {}

    MyClass(MyClass const&) = default;
    MyClass(MyClass &&) = default;
};

class OtherClass {
public:
    operator MyClass<int>() {
        return{};
    }
};

int main(){
    MyClass<int> mc;
    MyClass<int> mc2{ mc }; // error: cannot convert from 'MyClass<int>' to 'int'
    OtherClass oc;
    MyClass<int> mc3 {oc};  // error: cannot convert from 'OtherClass' to 'int'
}

How to correctly prevent the variadic template constructor from instantiating a copy/move constructor?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
W.H
  • 1,838
  • 10
  • 24
  • [See this thread](https://stackoverflow.com/questions/9287250/conflict-between-copy-constructor-and-forwarding-constructor) - you can use SFINAE or a dummy parameter to "delete" the copy-construction cases out of the forwarding constructor – M.M Dec 07 '17 at 02:12

1 Answers1

1

You can apply SFINAE with std::enable_if to restrict the types, e.g.

template <typename... Ts>
struct getFirstType {
    using type = void;
};
template <typename T, typename... Ts>
struct getFirstType<T, Ts...> {
    using type = T;
};

template<typename T>
class MyClass {
    T v;
public:
    // only valid when the first type of parameter pack is NOT MyClass
    template<typename...Ts, 
             typename = std::enable_if_t<
                 !std::is_same_v<MyClass, 
                                 std::decay_t<typename getFirstType<Ts...>::type>>>>
    MyClass(Ts&&...args) :v{ std::forward<Ts>(args)... } {}

    MyClass(MyClass const&) = default;
    MyClass(MyClass &&) = default;
};

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • You have added `MyClass() = default;` which doesn't initialize the member `v` while the variadic template constructor zero initializes it. – O'Neil Dec 07 '17 at 14:39