在C ++ 11中使用SFINAE在具有相同签名的两个函数之间进行选择

时间:2016-02-13 23:37:56

标签: c++ templates c++11 sfinae

我试图在c ++ 11中使用SFINAE在具有相同签名的两个函数之间进行选择,但是基于给定{内部应该存在typedef这一事实的不同主体{1}}类型。例如,我有两个结构,一个定义了MyTypedef,另一个没有任何结构:

T

然后我有两个模板函数,它们应该将结构的类型作为T,并根据struct MyStruct1 { typedef std::false_type MyTypedef; ... } struct MyStruct3 { ... } 的存在做不同的事情。特别是,如果定义了MyTypedef,我应该调用第一个函数,在所有其他情况下,我应该调用第二个函数。我试过这样的事情:

T::MyTypedef

但它没有编译,错误是C2668(MSVC) - '功能' :模糊调用重载函数

2 个答案:

答案 0 :(得分:5)

您可以编写一个特征来检测typedef是否存在,使用例如void_t和SFINAE基于:

template<class...> struct voider { using type = void; };
template<class... Ts> using void_t = typename voider<Ts...>::type;

template<class T, class = void>
struct has_MyTypedef : std::false_type {};

template<class T>
struct has_MyTypedef<T, void_t<typename T::MyTypedef>> : std::true_type {};

template<typename T, std::enable_if_t<has_MyTypedef<T>{}, int> = 0>
void myFunction()
{
    ...
}

template<typename T, std::enable_if_t<!has_MyTypedef<T>{}, int> = 0>
void myFunction()
{
    ...
}

或编写内部帮助程序,以便您可以使用虚拟参数在两种情况下消除它们之间的歧义:

template<class T, class = typename T::MyTypedef>
void myFunctionImpl(int) {
    // ...
}
template<class T>
void myFunctionImpl(...) {
    // ...
}

template<class T>
void myFunction(){
    return myFunctionImpl<T>(0);
}

答案 1 :(得分:1)

您的代码无法编译,因为在“has typedef”情况下,两个重载都存在且有效。编译器无法在它们之间进行选择。有三种基本方法。

首先,简单地反转条件。你用sfinae友好的方式编写“has typedef”和“没有typedef”,如果有,则启用一个函数,如果没有则启用另一个函数。

我发现这通常是丑陋的,并且规模很小。

第二种方法是通过使用重载解析顺序技巧(继承,变量,转换等)来使两者都有效时,首选方法是首选。这个尺度稍微好一点,但我发现它很硬,有点脆。

第三种方法是标签调度。这不支持“没有有效的过载”,但其他方面都是干净的。

你写了一个基本功能。此函数执行某些类型计算并使用结果构建一些标记类型。然后使用标记调用实现函数,并使用它来发送。

想象一下,你有特质has_foo<T>。如果类型std::true_type具有属性T,则继承自foo的类型,否则为false_type

然后我们可以:

 void bar_impl(std::true_type);
 void bar_impl(std::false_type);

template<class T>
void bar(T  const&){
  bar_impl( has_foo<T>{} );
}

和if bar_impl对应的T调用属性foo

编写这样的测试很容易。这是一个小图书馆:

 namespace details{
   template<template<class...>class Z, class, class...>
   struct can_apply:std::false_type{};
   template<template<class...>class Z, class...Ts>
   struct can_apply<Z, decltype(void(std::declval<Z<Ts...>>())), Ts...>:
     std::true_type
   {};
 }
 template<template<class...>class Z,class...Ts>
 using can_apply=typename details::can_apply<Z,void,Ts...>::type;

然后我们写下我们的测试:

template<class T>
using bob_subtype = typename T::bob;

类型T::bob。我们可以测试一下是否有东西:

template<class T>
using has_bob=can_apply<bob_subtype,T>;

完成了。