【问题标题】:如何根据模板类型名检查执行操作?
【发布时间】:2022-01-23 13:05:03
【问题描述】:

刚开始为我的一项任务探索模板功能,我需要根据模板中的类型名添加一些操作。有人能指出这种结构有什么问题吗:

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T>
T foo()
{
    if(std::is_same<T, int>::value)
    {
        return 2;
    }
    if(std::is_same<T, std::string>::value)
    {
        return "apple";
    }
}

int main()
{
    std::cout<<"foo is: "<<foo<int>()<<std::endl;
    return 0;
}

我想知道:

  1. 为什么这个错误会发生main.cpp:23:16: error: invalid conversion from ‘const char*’ to ‘int’ 以及如何摆脱它?
  2. 有没有更好的方法根据提供给函数的typename 来执行特定的操作

更新:

原来我的程序使用的是低于 C++17 的编译器

尝试:

我尝试了另一种方法来处理这种情况,但失败了:

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T, typename U>
T foo()
{
    T t = U;
    return t;
}

int main()
{
    std::cout<<"foo is: "<<foo<int, 1>()<<std::endl;
    return 0;
}

谁能指出这里出了什么问题?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    问题是,即使你的Tint,所有的分支还是要编译。所以第二个return语句会导致错误,因为字符串文字无法转换为int返回值。

    从 C++17 开始,您可以使用 if constexpr 告诉编译器条件是编译时常量,它只允许编译所需的分支:

    #include <iostream>
    #include <type_traits>
    
    using namespace std;
    
    template <typename T>
    T foo()
    {
        if constexpr(std::is_same<T, int>::value)
        {
            return 2;
        }
        if constexpr(std::is_same<T, std::string>::value)
        {
            return "apple";
        }
    }
    
    int main()
    {
        std::cout<<"foo is: "<<foo<int>()<<std::endl;
        return 0;
    }
    

    如果您需要早期标准中的解决方案,则必须像这样使用模板专业化:

    #include <iostream>
    #include <type_traits>
    
    using namespace std;
    
    //general template
    template <typename T>
    T foo();
    
    //specialization for int
    template <>
    int foo<int>()
    {
        return 2;
    }
    
    //specialization for string
    template <>
    string foo<string>()
    {
        return "apple";
    }
    
    int main()
    {
        std::cout<<"foo is: "<<foo<int>()<<std::endl;
        return 0;
    }
    

    【讨论】:

    • 看起来我没有 C++17 的:error: expected ‘(’ before ‘constexpr’,你能解释一下另一种方法吗?
    • 您是否将--std=c++17 添加到您的编译器命令中?无论如何,我会用其他版本编辑答案。
    • 如果我理解正确,这意味着我们必须在其相应的函数中为那个微粒typename 编写动作/逻辑?如果两者的大部分函数体相同怎么办?
    • 是的,您需要分离每种类型的实现。如果您有很多通用代码,最好只将特定部分分解到它们自己的函数中,然后从通用实现中调用它们。
    • 你写它的方式,U 也是一个类型,但你希望它是一个值。这对于某些类型是可能的,但不是全部。您可以将它作为常规参数传递,但现在它不再是完全编译时的:godbolt.org/z/7xv99z1hG 您采用哪种方式将取决于您的具体用例。
    【解决方案2】:

    模板实例化必须在所有分支上都有效,而您的则不是。我的意思是这就是您的模板生成的函数的样子:

    int foo<int>()
    {
        if(true)
        {
            return 2;
        }
        if(false)
        {
            return "apple"; // error
        }
    }
    
    std::string foo<std::string>()
    {
        if(false)
        {
            return 2; // error
        }
        if(true)
        {
            return "apple";
        }
    }
    

    从 C++17 开始,你可以使用 constexpr if 来解决这个问题:

    template <typename T>
    T foo()
    {
        if constexpr (std::is_same<T, int>::value)
        {
            return 2;
        }
        else if constexpr (std::is_same<T, std::string>::value)
        {
            return "apple";
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果您使用的是早期语言版本,则 C++17 的 constexpr-if 的另一种替代方法是标记调度:

      #include <iostream>
      #include <string>
      #include <type_traits>
      
      namespace detail {
      
      template <typename T> struct Tag {};
      
      int f_impl(Tag<int>) { return 2; }
      std::string f_impl(Tag<std::string>) { return "apple"; }
      
      } // namespace detail
      
      template <typename T> T foo() { return detail::f_impl(detail::Tag<T>{}); }
      
      int main() {
        std::cout << "foo is: " << foo<int>() << std::endl;
        return 0;
      }
      

      对于这个简单的例子,特化就可以了,但是标签分派对于更复杂的“if-else-dispatch”逻辑很有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        • 1970-01-01
        • 2019-11-10
        • 2017-03-08
        • 2015-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多