【问题标题】:C++ conditional execution depending on the typeC++ 条件执行取决于类型
【发布时间】:2021-12-20 13:38:22
【问题描述】:

我有一个模板函数,它应该执行不同的代码,具体取决于类型。简化后的函数如下所示:

template<typename T>
std::string test(T value)
{
    std::string v;
    if(std::is_arithmetic<T>())
    {
        v = std::to_string(value);
    }
    else
    {
       v = std::string(value);
    }
    return v;
}

用法:

test("Hello");
test(123);

但我收到此错误:

In instantiation of void test(T) [with T = const char*]:
error: no matching function for call to to_string(const char*)
note: candidate: std::string std::__cxx11::to_string(int) <near match>
to_string(int __val)

and the same for the following:

to_string(unsigned __val)
to_string(long __val)
to_string(unsigned long __val)

好的,我知道,例如const char *,编译将失败,因为没有std::to_string(const char *)。但我怎样才能使代码工作?只需要注意,在我的真实代码中,我限制为c++11

【问题讨论】:

  • if constexpr(std::is_arithmetic&lt;T&gt;())。如果你不能使用它,你需要使用 SFINAE 和两个不同的模板

标签: c++ c++11 templates


【解决方案1】:

您现在了解为什么将if constexpr 添加到该语言中。如果您需要在较大的算法中执行一些依赖于类型的操作,那么在 C++17 之前的版本中,执行此操作的方法通常是通过 tag-dispatch

namespace detail {
    template<typename T>
    std::string stringify(T value, std::true_type) {
        return std::to_string(value);
    }
    template<typename T>
    std::string stringify(T value, std::false_type) {
        return std::string(value);
    }
}

template<typename T>
std::string test(T value)
{
    std::string v;
    v = detail::stringify(value, std::is_arithmetic<T>());
    return v;
}

这是在两个条件下进行调度,但该技术可以扩展到多个重载,具体取决于您如何构建标签类型。标准中的一个常见示例是iterator categories

【讨论】:

  • 是的,如果我不限于使用 c++11,那就太好了,我们使用 gcc 4.8.2,因此 C++17 的功能不可用,很遗憾。
【解决方案2】:

您可以使用SFINAE 应用重载。例如

// for arithmetic types
template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type test(T value)
{
    std::string v;
    v = std::to_string(value);
    return v;
}

// for non-arithmetic types
template<typename T>
typename std::enable_if<!std::is_arithmetic<T>::value, std::string>::type test(T value)
{
    std::string v;
    v = std::string(value);
    return v;
}

【讨论】:

  • 好的,看起来 SFINAE 是我的情况的最佳解决方案,尽管我不太喜欢这种“模板技巧”而不是正常的内省。我很感激这个决定@songyuanyao
  • c++20 用 requires 代替 SFINAE 来清理它
【解决方案3】:

如果您想要 C++11 中可用的东西并且更接近您最初的尝试,您可以将 conditional_t 与辅助函子一起使用(一个用于算术类型,一个用于非算术类型)

namespace helper
{
template<typename T>
struct ARITHMETIC
{
    std::string operator()(T x)
    {
        return std::to_string(x);
    }
};

template<typename T>
struct NON_ARITHMETIC
{
    std::string operator()(T x)
    {
        return std::string(x);
    }
};
}

template <class T>
std::string test(T value)
{
    typename std::conditional<
        std::is_arithmetic<T>::value,
        helper::ARITHMETIC<T>,
        helper::NON_ARITHMETIC<T>>::type convert;

    return convert(value);
};

int main()
{
    std::cout << test("Hello") << std::endl;
    std::cout << test(123) << std::endl;
}

【讨论】:

    【解决方案4】:

    问题似乎是它检查它是否是整数的部分:

    // essentially the center of the code
        if (std::is_arithmetic<T>())
        {
            v = std::to_string(value);
        }
        else
        {
           v = std::string(value);
        }
    

    如果您使用的是 if constexpr(),但您不是。

    我还建议使用 SFINAE 或 tag-dispatch

    【讨论】:

      猜你喜欢
      • 2022-07-09
      • 1970-01-01
      • 2018-02-03
      • 2020-12-27
      • 1970-01-01
      相关资源
      最近更新 更多