如何根据模板类型名检查执行操作?

How to do actions based on template typename checks?

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

#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;
}

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

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

从 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;
}

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

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";
    }
}

如果您使用的是早期语言版本,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-分发”逻辑可能很有用。