当多个重载函数通过SFINAE时,创建一个首选的重载函数。

3
struct A { void a() { puts("a"); } };
struct B { void b() { puts("b"); } };
struct C : A, B {};

template <typename T> decltype(&T::a, (void)0) SFINAE(T t) { t.a(); }
template <typename T> decltype(&T::b, (void)0) SFINAE(T t) { t.b(); }

int foo()
{
    SFINAE(A{}); // works fine, calls a
    SFINAE(B{}); // works fine, calls b
    SFINAE(C{}); // compile error
}

上述代码在使用具有 ab 的类型调用 SFINAE 时失败,因此两个模板都有效,导致模糊不清的调用。如何修复上述代码以优先选择模糊情况下的第一个重载?因此,SFINAE(C{}) 应该调用 a
2个回答

3

我认为你需要表达的是:

  1. a 且没有 b
  2. b 且没有 a
  3. a 和有 b

例如:

template <typename T, typename = void>
struct has_a : std::false_type {};
template <typename T>
struct has_a<T, std::void_t<decltype(&T::a)>> : std::true_type {};

template <typename T, typename = void>
struct has_b : std::false_type {};
template <typename T>
struct has_b<T, std::void_t<decltype(&T::b)>> : std::true_type {};

template <typename T> std::enable_if_t<has_a<T>::value && !has_b<T>::value> SFINAE(T t) { t.a(); }
template <typename T> std::enable_if_t<has_b<T>::value && !has_a<T>::value> SFINAE(T t) { t.b(); }
template <typename T> std::enable_if_t<has_a<T>::value && has_b<T>::value> SFINAE(T t) { t.a(); }

实时演示

正如 @mch 建议的那样,您可以根据实际情况创建自己的条件组合。


3
你可以将第一个和第三个SFINAE函数结合起来,变成这样的形式:template <typename T> std::enable_if_t<has_a<T>::value> SFINAE(T t) { t.a(); }。链接为:https://wandbox.org/permlink/c7M8CFVXyzt3LuFJ - mch
@mch 不错的想法。无论如何,我会在答案中保留这个条件逻辑,因为它看起来更清晰,然后 OP 可以根据实际情况自己进行组合。 - songyuanyao

3

您可以通过引入转换来消除调用的歧义:

template <typename T> decltype(&T::a, (void)0) impl(T t, int) { t.a(); }
template <typename T> decltype(&T::b, (void)0) impl(T t, unsigned) { t.b(); }

template <typename T> void SFINAE(T && t) { impl(std::forward<T>(t), 42); }

传递类型为int42,编译器将选择第一个重载作为更好的匹配。

演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接