How to use SFINAE to select constructor from multiple options in C++11

人走茶凉 提交于 2019-12-06 07:16:32

Not ideal, but this gets the job done:

#include <iostream>
#include <type_traits>

template<typename T>
struct A {

    T data;

    A() : A((std::is_default_constructible<T> *)nullptr) {}

private:
    A(std::true_type *) { std::cout << "Data defaulted" << std::endl; }

    A(std::false_type *) : data(0) { std::cout << "Data zeroed" << std::endl; }
};

// struct which is not default-constructible
struct B {
    B() = delete;
    B(int) {}
};

int main()
{
    A<int> x; // int is default-constructible
    A<B> y; // class B is not default-constructible

    return 0;
}
m.s.

Aside from @Sam's tag dispatch solution you can use std::enable_if on the constructors, but you have to be aware of the following:

  1. the template parameter used for the std::is_default_constructible cannot be T, instead you need a "new" template parameter (which can be defaulted to T). See this SO question/answer for details: std::enable_if to conditionally compile a member function

  2. Quoting from cppreference.com:

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

This leads to the following code:

#include <iostream>
#include <type_traits>

template<typename T>
struct A {

    T data;

    // Valid if T is default-constructible; SFINAE otherwise
    template<typename X = T, typename SFINAE = typename std::enable_if<std::is_default_constructible<X>::value>::type, typename P = SFINAE>
    A() { std::cout << "Data defaulted" << std::endl; }


    // Valid if T is *not* default-constructible; SFINAE otherwise
    template<typename X = T, typename = typename std::enable_if<!std::is_default_constructible<X>::value>::type>
    A() : data(0) { std::cout << "Data zeroed" << std::endl; }
};

// struct which is not default-constructible
struct B {
    B() = delete;
    B(int) {}
};

int main()
{
    A<int> x; // int is default-constructible
    A<B> y; // class B is not default-constructible

    return 0;
}

live example

You could also create two alternative class implementations depending on the T and its default constructable ability:

#include <type_traits>
#include <iostream>

template <class T, class = void>
struct A_parent;

template <class T>
struct A_parent<T, typename std::enable_if<std::is_default_constructible<T>::value>::type> {
   T data;
   A_parent() {  std::cout << "Default constructable" << std::endl; }
};

template <class T>
struct A_parent<T, typename std::enable_if<!std::is_default_constructible<T>::value>::type> {
   T data;
   A_parent(): data(0) { std::cout << "Not default constructable" << std::endl; }
};

template <class T>
struct A: A_parent<T> {
   /* further implementation */
};

struct B {
    B() = delete;
    B(int) {}
};

int main() {
   A<int> a1;
   A<B> a2;
}

Output:

 Default constructable 
 Not default constructable
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!