为什么在这个例子中没有应用 SFINAE?
Why is SFINAE not applied in this example?
我希望通过 SFINAE 的应用程序编译以下最小示例:
#include <iostream>
#include <string>
#include <type_traits>
struct FallBack{};
struct S {
int bar() { return 42; }
};
template <typename A, typename B>
std::enable_if_t< (std::is_same< std::enable_if_t<true==FLAG, FallBack>, FallBack>::value and std::is_convertible<B, std::string>::value), int >
foo ( A a) { return a.bar();
}
template <typename A, typename B>
std::enable_if_t<false==FLAG, int>
foo ( A a) { std::cout << "false solution " << std::endl; return -1; }
int main()
{
std::cout << foo<S, std::string>( S{}) << std::endl;
}
编译:
g++ -std=c++14 -DFLAG=1 myFile.cc
如果我评论第二个foo
函数,一切正常,因此我对SFINAE的理解是错误的;此外,编译器抱怨 foo
.
的定义含糊不清
我显然错了,但看不出问题所在。
能否请任何人评论并解释为什么不应用 SFINAE?
您有非模板依赖条件。
你应该这样做
template <typename A, typename B>
std::enable_if_t<std::is_same<A, A>::value == FLAG, int>
foo (A a) { std::cout << "false solution " << std::endl; return -1; }
为什么不直接使用:
#if FLAG == 1
template <typename A, typename B>
std::enable_if_t<std::is_convertible<B, std::string>::value, int >
foo ( A a) { return a.bar();
}
#else
template <typename A, typename B>
int foo ( A a) { std::cout << "false solution " << std::endl; return -1; }
#endif
鉴于您无论如何都在使用编译时定义,使用它来显式指定您需要的定义而不是完全依赖 SFINAE 是有意义的。
正如@Jarod42 所说,sfinae 表达式不适用于非模板依赖条件。
无论如何,如果您可以将它分成两个结构,这里有一个解决方案:
#include <iostream>
#include <string>
#include <type_traits>
struct S {
int bar() { return 42; }
};
template <typename B, bool = FLAG>
struct T {
template<typename A>
static void foo ( A a ) {
static_assert(std::is_convertible<B, std::string>::value, "!");
std::cout << a.bar() << std::endl;
}
};
template <typename B>
struct T<B, false> {
template<typename A>
static void foo( A a ) {
static_assert(std::is_convertible<B, std::string>::value, "!");
std::cout << "false solution " << std::endl;
}
};
int main() {
T<std::string>::foo(S{});
T<std::string, true>::foo(S{});
T<std::string, false>::foo(S{});
}
它基于部分专业化而不是 sfinae。
此解决方案的缺点(或者它是一个功能?)是您仍然可以通过显式强制 bool
参数来解决它和 FLAG
的值。
有关详细信息,请参阅上面的代码。
我希望通过 SFINAE 的应用程序编译以下最小示例:
#include <iostream>
#include <string>
#include <type_traits>
struct FallBack{};
struct S {
int bar() { return 42; }
};
template <typename A, typename B>
std::enable_if_t< (std::is_same< std::enable_if_t<true==FLAG, FallBack>, FallBack>::value and std::is_convertible<B, std::string>::value), int >
foo ( A a) { return a.bar();
}
template <typename A, typename B>
std::enable_if_t<false==FLAG, int>
foo ( A a) { std::cout << "false solution " << std::endl; return -1; }
int main()
{
std::cout << foo<S, std::string>( S{}) << std::endl;
}
编译:
g++ -std=c++14 -DFLAG=1 myFile.cc
如果我评论第二个foo
函数,一切正常,因此我对SFINAE的理解是错误的;此外,编译器抱怨 foo
.
我显然错了,但看不出问题所在。 能否请任何人评论并解释为什么不应用 SFINAE?
您有非模板依赖条件。
你应该这样做
template <typename A, typename B>
std::enable_if_t<std::is_same<A, A>::value == FLAG, int>
foo (A a) { std::cout << "false solution " << std::endl; return -1; }
为什么不直接使用:
#if FLAG == 1
template <typename A, typename B>
std::enable_if_t<std::is_convertible<B, std::string>::value, int >
foo ( A a) { return a.bar();
}
#else
template <typename A, typename B>
int foo ( A a) { std::cout << "false solution " << std::endl; return -1; }
#endif
鉴于您无论如何都在使用编译时定义,使用它来显式指定您需要的定义而不是完全依赖 SFINAE 是有意义的。
正如@Jarod42 所说,sfinae 表达式不适用于非模板依赖条件。
无论如何,如果您可以将它分成两个结构,这里有一个解决方案:
#include <iostream>
#include <string>
#include <type_traits>
struct S {
int bar() { return 42; }
};
template <typename B, bool = FLAG>
struct T {
template<typename A>
static void foo ( A a ) {
static_assert(std::is_convertible<B, std::string>::value, "!");
std::cout << a.bar() << std::endl;
}
};
template <typename B>
struct T<B, false> {
template<typename A>
static void foo( A a ) {
static_assert(std::is_convertible<B, std::string>::value, "!");
std::cout << "false solution " << std::endl;
}
};
int main() {
T<std::string>::foo(S{});
T<std::string, true>::foo(S{});
T<std::string, false>::foo(S{});
}
它基于部分专业化而不是 sfinae。
此解决方案的缺点(或者它是一个功能?)是您仍然可以通过显式强制 bool
参数来解决它和 FLAG
的值。
有关详细信息,请参阅上面的代码。