A"全能"对于SFINAE功能?

时间:2017-12-04 19:29:01

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

我正在尝试创建一个类型化比较函数,为不同类型进行一些自定义比较。

function Test() {
    this.myTest = "Test";
}

Test.prototype.toString = function testToString() {
    return this.myTest;
};

var test = new Test();
console.log(test.toString());

这里我有 #include <type_traits> template <typename T> bool typedCompare(const T& lhs, const T& rhs) { return lhs == rhs; // default case, use == } template <typename T> typename std::enable_if<std::is_floating_point<T>::value, bool>::type typedCompare(const T& lhs, const T& rhs) { return (lhs - rhs) < 1e-10; } int main() { typedCompare(1, 1); typedCompare(1.0, 1.0); return 0; } 的特殊版本,可以将差异与少量进行比较(请忽略我没有使用double这一事实)。我还有一些其他自定义类型需要进行一些特殊比较,而且出于某种原因我无法更改std::abs()运算符。

此外,我仍然想要一个&#34;全能&#34;使用==运算符的样式函数。我的问题是,在尝试编译此代码片段时,编译器抱怨==不明确,它可以选择提供的两个函数中的任何一个。

为什么呢?我怎么能解决这个问题?

感谢。

3 个答案:

答案 0 :(得分:6)

  

为什么?

简而言之,您使用的SFINAE不正确,因此当您为双打调用typedCompare时,这两个功能模板都有效。

  

我该如何解决这个问题?

在这种特殊情况下,修复SFINAE以使其正常工作:

template <typename T>
typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
    return lhs == rhs; // default case, use ==
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
    return (lhs - rhs) < 1e-10;
}

请注意,此解决方案在许多类型的自定义方面不是那么好。另一种方法是使用标签调度:

struct floating_point_tag {};
// other tags

template <typename T>
bool typedCompare(const T& lhs, const T& rhs, floating_point_tag) {
    return (lhs - rhs) < 1e-10;
}

// implementations for other tags

template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
    if (std::is_floating_point<T>::value) {
        return typedCompare(lhs, rhs, floating_point_tag{});
    }

    // other checks here

    return lhs == rhs;
}

最后,使用C ++ 17,您可以使用if constexpr

template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
    if constexpr (std::is_floating_point<T>::value) {
        return (lhs - rhs) < 1e-10;
    } else { // add other if-else here
        return lhs == rhs;
    }
}

答案 1 :(得分:2)

您的问题中的代码问题是,当浮点typedCompare()启用SFINAE时,会与通用版本冲突,因为编译器不能优先于另一个版本。

要解决这个问题,我建议您采用另一种方式,基于模板部分特化(仅适用于结构和类,因此需要帮助struct

如果您按如下方式定义帮助struct

template <typename T, typename = void>
struct typedCompareHelper
 {
   static constexpr bool func (T const & lhs, T const & rhs)
    { return lhs == rhs; } // default case, use == 
 };

template <typename T>
struct typedCompareHelper<T,
   typename std::enable_if<std::is_floating_point<T>::value>::type>
 {
   static constexpr bool func (T const & lhs, T const & rhs)
    { return (lhs - rhs) < 1e-10; }
 };

您可以避免歧义问题,因为typedCompareHelper专业化更专业化的泛型问题。

您可以简单地为不同的特殊情况添加更多特化,只关注避免碰撞(适用于同一类型的同一级别的不同特化)。

您的typedCompare()变得简单

template <typename T>
bool typedCompare (T const & lhs, T const & rhs)
 { return typedCompareHelper<T>::func(lhs, rhs); }

答案 2 :(得分:1)

我喜欢@ max66的解决方案和辅助模板以及@Edgar Rokyan的多种解决方案。

这是另一种可以使用辅助模板函数用于所需内容的方法。

#include <type_traits>
#include <iostream>
#include <string>

// elipsis version is at the bottom of the overload resolution priority.
// it will only be used if nothing else matches the overload.
void typeCompare_specialized(...)
{
  std::cout << "SHOULD NEVER BE CALLED!!!\n";
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
  std::cout << "floating-point version\n";
  return (lhs - rhs) < 1e-10;
}

template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
  std::cout << "integral version\n";
  return lhs == rhs;
}

template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
  -> typename std::enable_if<std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
  return typeCompare_specialized(lhs, rhs);
}

template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
  -> typename std::enable_if<!std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
  std::cout << "catch-all version\n";
  return lhs == rhs;
}

int main()
{
  typedCompare(1, 1);
  typedCompare(1.0, 1.0);
  typedCompare(std::string("hello"), std::string("there"));

  return 0;
}

运行上述程序将产生以下输出:

integral version
floating-point version
catch-all version

同样,我更愿意使用前面提到的答案之一。我把这种可能性包括在内。

我还想补充一点,你应该确保你的typeCompare_specialized()模板版本不应该有任何重叠,否则你可能会收到一个编译器错误声明有多个候选重载要使用。