条件编译和非类型模板参数

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。编译器抱怨,因为在这样的转换中精度会丢失。

检查 intconst 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