【问题标题】:How does SFINAE exactly work?SFINAE 究竟是如何运作的?
【发布时间】:2018-02-07 22:21:48
【问题描述】:

我对 SFINAE 有疑问。我错过了一些重要的东西,阅读其他问题也无济于事:(

代码如下:

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

// version with template class

template<typename T, typename = std::enable_if_t<
                std::is_integral<std::remove_reference_t<T>>::value
             || std::is_floating_point<std::remove_reference_t<T>>::value
             >
         >
struct to_my_string
{
    std::string operator()(T numerical)
    {
        return std::to_string(numerical);
    }
};

template<typename T, typename = std::enable_if_t< std::is_same<T, std::string>::value > >
struct to_my_string
{
    std::string operator()(T str)
    {
        return "'" + str + "'";
    }
};


// version with template method

template<typename T, typename = std::enable_if_t<
                std::is_integral<std::remove_reference_t<T>>::value
             || std::is_floating_point<std::remove_reference_t<T>>::value
             >
         >
std::string to_string_method(T numerical)
{
    return std::to_string(numerical);
}

template<typename T, typename = std::enable_if_t< std::is_same<T, std::string>::value > >
std::string to_string_method(T str)
{
    return "'" + str + "'";
}

int main()
{
    std::cout << "start" << std::endl;
    return 0;
}

我已经粘贴了两种方法 - 一个类和一个方法方法。两者都产生类似的错误: 以下是 g++ 的错误(clang 给出了类似的错误):

g++ --std=c++14 sfinae_failure.cpp

sfinae_failure.cpp:21:12: error: redefinition of default argument for ‘class<template-parameter-1-2>’
 struct to_my_string
        ^
sfinae_failure.cpp:7:26: note: original definition appeared here
 template<typename T, typename = std::enable_if_t<
                      ^
sfinae_failure.cpp:43:17: error: redefinition of ‘template<class T, class> std::__cxx11::string to_string_method(T)’
 std::string to_string_method(T str)
             ^
sfinae_failure.cpp:37:17: note: ‘template<class T, class> std::__cxx11::string to_string_method(T)’ previously declared here
 std::string to_string_method(T numerical)
             ^

我的主要问题是为什么它不起作用,即使我在 enable_if_t 中使用 T 类型。我错过了什么?

附加的问题是 - 我认为模板类/方法在使用时会被实例化。正如您在 main 方法中看到的那样 - 它们不是。

即使这个话题被多次触及,我也非常感谢这里的帮助......

【问题讨论】:

  • 一个常见的错误是声明两个仅在默认模板参数上有所不同的函数模板。这是非法的,因为默认模板参数不是函数模板签名的一部分,并且用相同的签名声明两个不同的函数模板是非法的en.cppreference.com/w/cpp/types/enable_if
  • 对于基于结构的版本,您需要有一个基础模板(在这种情况下可能是一个空结构),然后为您的情况专门
  • 抛开SFINAE,您没有正确实现模板专业化。首先了解如何进行模板专业化,然后尝试将 SFINAE 添加到您的专业化中。
  • @DrewDormann,我想我昨天在类模板专业化方面有点停电:|

标签: c++ sfinae enable-if


【解决方案1】:

你的类模板的错误是因为你不能重新定义一个类模板,它与 SFINAE 无关。

template<class A = int> struct foo {};
template<class A = double> struct foo {};

即使没有实例化,这段代码也无法编译。

您的函数的错误是因为默认模板参数不是函数模板参数签名的一部分。

template<class A, class B = int> void bar() {}
template<class A, class B = double> void bar() {}

这段代码即使没有实例化也无法编译,因为它们的模板参数签名相同:

template<class A, class B> void bar() {}

因此重新定义。

【讨论】:

  • 谢谢!我不知道为什么我开始尝试覆盖基本模板而不是专注于部分专业化。虽然这些方法对我来说仍然是一个谜——下面的解决方案有效,但我不确定为什么我必须这样做。更多内容在下面的评论中。
  • 在下面的解决方案中, enable_if_t 成为非类型模板参数,因此 2 个函数具有本质上不同的模板参数。你原来的解决方案 enable_if_t 只是一个默认的参数,模板参数是一样的(都是匿名类型参数)。
【解决方案2】:

固定版本:

template<typename T, typename Enabler = void> struct to_my_string;

template <typename T>
struct to_my_string<T, std::enable_if_t<
                std::is_integral<std::remove_reference_t<T>>::value
             || std::is_floating_point<std::remove_reference_t<T>>::value
             >>
{
    std::string operator()(T numerical) const
    {
        return std::to_string(numerical);
    }
};

template<typename T>
struct to_my_string<T, std::enable_if_t<std::is_same<T, std::string>::value>>
{
    std::string operator()(const T& str) const
    {
        return "'" + str + "'";
    }
};


// version with template method
template<typename T, std::enable_if_t<
                std::is_integral<std::remove_reference_t<T>>::value
             || std::is_floating_point<std::remove_reference_t<T>>::value
             , void*> = nullptr
         >
std::string to_string_method(T numerical)
{
    return std::to_string(numerical);
}

template<typename T, std::enable_if_t< std::is_same<T, std::string>::value, void*> = nullptr>
std::string to_string_method(T str)
{
    return "'" + str + "'";
}

【讨论】:

  • 谢谢!我想我理解我在这两种情况下的错误。
猜你喜欢
  • 2019-07-13
  • 1970-01-01
  • 2011-06-26
  • 2021-08-15
  • 2012-06-08
  • 2011-10-11
  • 2013-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多