条件编译和非类型模板参数
Conditional compilation and non-type template parameters
我在理解非类型模板参数时遇到问题,希望有人能阐明这一点。
#include <iostream>
template<typename T, int a>
void f() {
if (a == 1) {
std::cout << "Hello\n";
} else {
T("hello");
}
}
int main() {
f<int, 1>;
}
当我编译这个时,我收到一条错误消息:
/tmp/conditional_templates.cc:13:12: required from here
/tmp/conditional_templates.cc:8:5: error: cast from ‘const char*’ to ‘int’ loses precision [-fpermissive]
T("hello");
^
但是,编译器不能检测到非类型参数 "a" 是 1,因此不会采用 else 分支吗?还是期望值过高?在这种情况下,我该如何完成这样的事情?
试试这个:
#include <iostream>
template<typename T, int a>
struct A {
void f() {
T("hello");
}
};
template<typename T>
struct A<T,1> {
void f() {
std::cout << "Hello\n";
}
};
int main() {
A<int,1> a;
a.f();
A<int,2> b;
b.f();
}
现在,这使用 partial template specialization 来为模板参数的特定值提供替代实现。
请注意,我使用了 class,因为 function templates cannot be partially specialized
似乎在您的环境中 sizeof(int)
与 sizeof(const char*)
不同。
在这种情况下,部分代码:T("hello")
实际上是 int("hello")
试图将 const char*
转换为 int
。编译器抱怨,因为在这样的转换中精度会丢失。
检查 int
和 const char*
的大小:
std::cout << sizeof(int) << std::endl;
std::cout << sizeof(const char*) << std::endl;
else
分支在调用 f<int, 1>()
时不会是 运行 但这并不意味着编译器将忽略 else
代码中的错误。
我不得不承认,老实说,我看不出这样做的根本原因,但这是您的代码。除了明显的错误(未能为 main()
中的函数调用提供括号和警告(将 char 地址转换为 int
的数据丢失),关于条件包含的更大问题很重要。
如果你有这样的代码:
if (something)
{
do something
}
它显然必须编译,并且不会有条件地这样做。 something
来自非类型模板参数没有区别。您需要将逻辑从函数中的 if 表达式中取出,并改为使用模板扩展控制机制。专业化就是这样一种技术,SFINAE 是另一种:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <cstdint>
static const char* something = "something";
template<class T, bool a>
typename std::enable_if<a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << something << '\n';
}
template<class T, bool a>
typename std::enable_if<!a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << std::hex << T(something) << '\n';
}
int main()
{
f<int, true>();
f<intptr_t, false>();
}
输出
typename std::enable_if<a>::type f() [T = int, a = true]
something
typename std::enable_if<!a>::type f() [T = long, a = false]
100001f18
你在每个方面做什么取决于你。当然,您可以使用预处理器宏进行 much/all 操作,但这样做有什么乐趣呢?
2022年这已经不是什么大秘密了,不过我还是觉得值得一提。
有了 C++17 中的 constexpr if,您真正想要使用的工具就来了。实例化模板时,不会实例化模板化实体中每个丢弃的 constexpr if 语句。 (有例外)
您的代码片段只需要稍作修改。 (除了修复)主要是必须反转分支以将麻烦的语句放入 constexpr if 语句中。
#include <iostream>
template<typename T, int a>
void f() {
if constexpr (a != 1) {
T("foo");
} else {
std::cout << "bar\n";
}
}
struct Printer
{
Printer(char const * const msg) {
std::cout << "Message: " << msg << '\n';
}
};
int main() {
f<int, 1>();
f<Printer, 0>();
}
这会打印
bar
Message: foo
我在理解非类型模板参数时遇到问题,希望有人能阐明这一点。
#include <iostream>
template<typename T, int a>
void f() {
if (a == 1) {
std::cout << "Hello\n";
} else {
T("hello");
}
}
int main() {
f<int, 1>;
}
当我编译这个时,我收到一条错误消息:
/tmp/conditional_templates.cc:13:12: required from here
/tmp/conditional_templates.cc:8:5: error: cast from ‘const char*’ to ‘int’ loses precision [-fpermissive]
T("hello");
^
但是,编译器不能检测到非类型参数 "a" 是 1,因此不会采用 else 分支吗?还是期望值过高?在这种情况下,我该如何完成这样的事情?
试试这个:
#include <iostream>
template<typename T, int a>
struct A {
void f() {
T("hello");
}
};
template<typename T>
struct A<T,1> {
void f() {
std::cout << "Hello\n";
}
};
int main() {
A<int,1> a;
a.f();
A<int,2> b;
b.f();
}
现在,这使用 partial template specialization 来为模板参数的特定值提供替代实现。
请注意,我使用了 class,因为 function templates cannot be partially specialized
似乎在您的环境中 sizeof(int)
与 sizeof(const char*)
不同。
在这种情况下,部分代码:T("hello")
实际上是 int("hello")
试图将 const char*
转换为 int
。编译器抱怨,因为在这样的转换中精度会丢失。
检查 int
和 const char*
的大小:
std::cout << sizeof(int) << std::endl;
std::cout << sizeof(const char*) << std::endl;
else
分支在调用 f<int, 1>()
时不会是 运行 但这并不意味着编译器将忽略 else
代码中的错误。
我不得不承认,老实说,我看不出这样做的根本原因,但这是您的代码。除了明显的错误(未能为 main()
中的函数调用提供括号和警告(将 char 地址转换为 int
的数据丢失),关于条件包含的更大问题很重要。
如果你有这样的代码:
if (something)
{
do something
}
它显然必须编译,并且不会有条件地这样做。 something
来自非类型模板参数没有区别。您需要将逻辑从函数中的 if 表达式中取出,并改为使用模板扩展控制机制。专业化就是这样一种技术,SFINAE 是另一种:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <cstdint>
static const char* something = "something";
template<class T, bool a>
typename std::enable_if<a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << something << '\n';
}
template<class T, bool a>
typename std::enable_if<!a>::type f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << std::hex << T(something) << '\n';
}
int main()
{
f<int, true>();
f<intptr_t, false>();
}
输出
typename std::enable_if<a>::type f() [T = int, a = true]
something
typename std::enable_if<!a>::type f() [T = long, a = false]
100001f18
你在每个方面做什么取决于你。当然,您可以使用预处理器宏进行 much/all 操作,但这样做有什么乐趣呢?
2022年这已经不是什么大秘密了,不过我还是觉得值得一提。
有了 C++17 中的 constexpr if,您真正想要使用的工具就来了。实例化模板时,不会实例化模板化实体中每个丢弃的 constexpr if 语句。 (有例外)
您的代码片段只需要稍作修改。 (除了修复)主要是必须反转分支以将麻烦的语句放入 constexpr if 语句中。
#include <iostream>
template<typename T, int a>
void f() {
if constexpr (a != 1) {
T("foo");
} else {
std::cout << "bar\n";
}
}
struct Printer
{
Printer(char const * const msg) {
std::cout << "Message: " << msg << '\n';
}
};
int main() {
f<int, 1>();
f<Printer, 0>();
}
这会打印
bar
Message: foo