简体   繁体   中英

Unused constructor instantiated in template class created by another constructor

I have the following class:

template <typename T=void>
class Foo{
public:

  Foo(){};

  template <typename = typename std::enable_if_t<!std::is_void<T>::value, std::nullptr_t>>
  Foo(const T&){};

};

int main() {
  Foo<void> v;
}

v is created using the first constructor. Therefore, there is no need to create the second constructor for Foo<void> .

Why does it get created anyways?

The problem is that explicitly creating the second constructor with the type void bypasses SFINAE, and tries to make a parameter of const void& . This is obviously not allowed.

How can I prevent the second constructor from being valid if T is void ?

Why does it get created anyways?

Because in your template constructor

template <typename = typename std::enable_if_t<!std::is_void<T>::value, std::nullptr_t>>
Foo(const T&){};

the value of the test for std::enable_if ( :std::is_void<T>::value ) depends from the template type of the class ( T ).

To SFINAE enable/disable a method of a class (or struct) you have to write a test that depends from a template parameter of the method itself.

A way to circumvent this problem is add a template parameter U for the method and give it T as default type. I mean something as

template <typename U = T,
          typename = std::enable_if_t<!std::is_void<U>::value, std::nullptr_t>>
Foo(const U&){} // ..... the test depends from U ---^
//        ^--- U also here, to avoid the void reference problem

or, maybe better,

template <typename U = T, 
          std::enable_if_t<!std::is_void<U>::value, std::nullptr_t> = nullptr>
Foo(const U&){}

An alternative to disabling the constructor is to replace const T& with some unusable type when T is void:

struct unusable {
  unusable() = delete;
  unusable(const unusable&) = delete;
  ~unusable() = delete;
};

template <typename T=void>
class Foo{
  using cref = std::conditional_t<std::is_void_v<T>, unusable, std::add_lvalue_reference_t<const T>>;
public:

  Foo(){}

  Foo(cref){}

};

This is useful const T& it is used in multiple places so you don't have to use SFINAE everywhere.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM