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

3

我对非类型模板参数感到困惑,希望有人能够解释一下。

#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分支不会被执行吗?还是这太难了?在这种情况下,我该如何实现类似的功能?


我理解这个错误并且有一个修复方法。但是,我不明白你想做什么?在你的模板参数列表中,a代表什么?为什么你需要它? - Nawaz
在函数f()内,从语句T("Hello")中删除"T"。将非类型作为模板参数传递是完全可以的。在main()函数中的函数调用应该是f<int, 1>()。 - MNS
我认为OP想要将else条件设置为不可能,而且他想知道为什么编译器不会忽略错误,因为第二个模板参数1将触发执行if语句而不是else语句。 - chrisb2244
我想根据变量的值有条件地编译代码。也许在我的示例中,我应该使用bool而不是int。 - Anirudh
@Anirudh 你正在寻找“静态if”。目前它还没有出现在C++中。这个工作组文档是一个有用的阅读材料。 - Pradhan
显示剩余2条评论
4个回答

3

我必须承认,我真的不明白为什么要这样做的根本原因,但这是你的代码。除了明显的错误(在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

你在每个环节中做什么取决于你。当然,你可以使用预处理宏来完成大部分或全部工作,但这样做有何乐趣呢?


1
尝试使用这个替代方案:
#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();
}

现在,这里使用部分模板特化以便为模板参数的具体值提供替代实现。
请注意,我使用了一个类,因为函数模板无法进行部分特化

0

在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

0

看起来在您的环境中,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 代码中的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接