gpt4 book ai didi

c++ - 为什么 SFINAE 在默认函数参数的右侧不起作用?

转载 作者:可可西里 更新时间:2023-11-01 15:48:39 24 4
gpt4 key购买 nike

我有这个代码:

struct My
{
typedef int foo;
};

struct My2
{
};


template <typename T>
void Bar(const T&, int z = typename T::foo())
{
std::cout << "My" << std::endl;
}


void Bar(...)
{
std::cout << "..." << std::endl;
}

int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Compile error: no type named ‘foo’ in ‘struct My2’
return 0;
}

我想,如果某个类 T 内部没有 typedef foo,编译器应该排除第一个重载并选择带省略号的重载。但是我在 MSVC、gcc 和 clang 上检查了这段代码,我在这些编译器上遇到了编译错误。为什么 SFINAE 在这种情况下不起作用?

最佳答案

z 的类型不受模板替换的影响,它总是int。这意味着 SFINAE 没有机会,而是在尝试将 T::foo 解析为默认值时出现编译器错误。默认参数不参与重载决议,而是仅在函数调用中丢失时才实例化。该标准的第 14.7.1 节(第 13/14 段)描述了这种行为,但没有给出此处缺少 SFINAE 的理由。

通过将 z 的类型作为模板参数,可以允许 SFINAE 发生,如下所示:

(实例:http://ideone.com/JynMye)

#include <iostream>

struct My
{
typedef int foo;
};

struct My2
{
};

template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}

void Bar(...)
{
std::cout << "...\n";
}

int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}

第一次调用将使用“My”版本,第二次调用将使用“...”版本。输出是

My
...

但是,如果 void Bar(...) 是一个模板,无论出于何种原因,“我的”版本永远不会有机会:

(实例:http://ideone.com/xBQiIh)

#include <iostream>

struct My
{
typedef int foo;
};

struct My2
{
};

template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}

template<typename T> void Bar(T&)
{
std::cout << "...\n";
}

int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}

此处,“...”版本在两种情况下均被调用。输出是:

...
...

一种解决方案是使用类模板(部分)特化;提供“...”版本作为基础,第二个参数的类型默认为 int,“My”版本作为特化,其中第二个参数为 typename T: :foo。结合一个简单的模板函数来推导 T 并分派(dispatch)给适当的类的成员函数,这会产生预期的效果:

(实例:http://ideone.com/FanLPc)

#include <iostream>

struct My
{
typedef int foo;
};

struct My2
{
};

template<typename T, typename I=int> struct call_traits {
static void Bar(...)
{
std::cout << "...\n";
}
};

template<typename T> struct call_traits<T, typename T::foo> {
static void Bar(const T&, int z=typename T::foo())
{
std::cout << "My\n";
}
};

template<typename T> void Bar(const T& t)
{
call_traits<T>::Bar(t);
}

int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Still OK
return 0;
}

这里,输出是:

My
...

关于c++ - 为什么 SFINAE 在默认函数参数的右侧不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24909652/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com