如何根据模板类型名检查执行操作?
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;
}
我在想:
- 为什么会出现这种 错误
main.cpp:23:16: error: invalid conversion from ‘const char*’ to ‘int’
以及如何消除它?
- 有没有更好的方法来根据提供给函数的
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;
}
谁能指出这里出了什么问题?
问题是,即使你的T
是int
,所有的分支仍然需要编译。所以第二个 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-分发”逻辑可能很有用。
刚开始为我的一项任务探索模板功能,我需要根据模板中的类型名称添加一些操作。有人可以指出这种结构有什么问题吗:
#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;
}
我在想:
- 为什么会出现这种 错误
main.cpp:23:16: error: invalid conversion from ‘const char*’ to ‘int’
以及如何消除它? - 有没有更好的方法来根据提供给函数的
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;
}
谁能指出这里出了什么问题?
问题是,即使你的T
是int
,所有的分支仍然需要编译。所以第二个 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-分发”逻辑可能很有用。