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

Conditional compilation and non-type template parameters

本文关键字:参数 类型 编译 条件      更新时间:2023-10-16

我在理解非类型模板参数时遇到困难,希望有人能阐明这一点。

#include <iostream>
template<typename T, int a>
void f() {
  if (a == 1) {
    std::cout << "Hellon";
  } 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 分支吗?还是期望太高了?在这种情况下,我如何完成这样的事情?

我不得不承认,老实说,我没有看到这样做的根本原因,但它是你的代码。除了明显的错误(未能为 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

在每个地方做什么取决于你。当然,您可以使用预处理器宏进行大量/所有这些操作,但是其中的乐趣在哪里?

试试这个:

#include <iostream>
template<typename T, int a>
struct A {
    void f() {
        T("hello");
    }
};
template<typename T>
struct A<T,1> {
    void f() {
        std::cout << "Hellon";
    }
};

int main() {
  A<int,1> a;
  a.f();
  A<int,2> b;
  b.f();
}

现在,这使用部分模板专用化,以便为模板参数的特定值提供替代实现。

请注意,我使用了一个类,因为函数模板不能部分专用

似乎在您的环境中sizeof(int)sizeof(const char*)不同。

在这种情况下,代码的一部分:实际上是int("hello") T("hello")尝试将const char*转换为int。编译器抱怨因为在这种转换中会丢失精度。

要检查intconst char*的大小:

std::cout << sizeof(int) << std::endl;
std::cout << sizeof(const char*) << std::endl;

调用else分支时不会运行f<int, 1>()但这并不意味着编译器将忽略else代码中的错误。

在 2022 年,这不再是一个大秘密,但我仍然认为这值得一提。

使用

constexpr 如果在 C++17 中您实际想要使用的工具到达了。模板化实体内每个丢弃的 constexpr if 语句在实例化模板时不会实例化。(例外情况适用)

您的代码段只需要稍作修改。(除了修复)主要是分支必须反转才能将麻烦的语句放入 constexpr if 语句中。

#include <iostream>
template<typename T, int a>
void f() {
  if constexpr (a != 1) {
    T("foo");
  } else {
    std::cout << "barn";
  }
}
struct Printer
{
    Printer(char const * const msg) {
        std::cout << "Message: " << msg << 'n';
    }
};
int main() {
  f<int, 1>();
  f<Printer, 0>();
}

这打印

bar
Message: foo