从字符串字面值进行模板参数推导

6

我目前正在考虑如何最好地约束模板的通用类型为std::string以及字符串字面值。因此,我使用std::is_same将推导出的类型与所需类型进行比较。对于std::string,这种方法立即奏效。对于字符串字面值,即char const数组,只有当我在类型上使用std::decay,然后将结果与类型char const *进行比较时,才能正常工作。如果我直接将推导出的类型与我认为的类型进行比较,is_same返回false,如下面的示例代码所示。

template <class TYPE>
void function(TYPE&& parameter)
{
  //this doesn't work as expected
  std::cout << typeid(TYPE).name() << " : " << typeid(char const [5]).name() << std::endl;
  std::cout << std::is_same<char const [5], TYPE>::value << std::endl;
  //this works as expected
  std::cout << typeid(std::decay_t<TYPE>).name() << " : " << typeid(char const *).name() << std::endl;
  std::cout << std::is_same<char const *, std::decay_t<TYPE>>::value << std::endl;
}

int main(int argc, char** argv)
{
  function("name");
  return 0;
}

生成的输出如下:
char const [5] : char const [5]
0
char const * __ptr64 : char const * __ptr64
1

现在我想知道的是,为什么即使类型看起来完全相同,在第一个情况下is_same返回false。

我能想到的唯一可能的解释是,在函数std::is_same内部应用了类似于std::decay的转换到类型上(例如函数调用)。但是再一次地,这种转换也会发生在另一个类型上,得出相同的结果,从而导致相等。


等到C++17,然后编写您的模板以使用std::string_view - Sam Varshavchik
1
尝试从 TYPE 中删除引用。字符串文字是 lvalue。 - Kerrek SB
谢谢,你是对的。我不知道那个。将该行更改为std::is_same<char const [5], std::remove_reference_t<TYPE>>::value会导致相等。为什么字符串字面值被认为是左值?如果它是一个int字面值,它会被认为是右值,不是吗?我们可以把这个作为接受的答案吗? - bweber
3个回答

6

字符串字面量并不是通过传值的方式作为 char const [N] 传递,而是通过引用的方式作为 char const (&)[N] 传递。

对我来说,这个方法运行正常:

std::cout << std::is_same<char const (&)[5], TYPE>::value << std::endl;

请注意,这里指的是:

1) 引用表示类型type的std::type_info对象。 如果type是引用类型,则结果引用一个表示所引用类型的std::type_info对象

你可以通过检查以下内容轻松验证is_same不会像type_info一样丢弃引用性,例如:

std::is_same<int&, int>::value == false

这就解释了为什么typeid名称相同,但您的is_same测试仍然失败。

2

使用gcc自定义函数:

template < class T >
constexpr std::string type_name()
{
    std::string p = __PRETTY_FUNCTION__;
    return p.substr( 43 + 10, p.length() - 100 - 1 - 10 );
}

并将其添加到您的代码中:

std::cout << type_name<TYPE>() << " : " << type_name<char const [5]>() << std::endl;

结果如下:
A5_c : A5_c
0
const char (&)[5] : const char [5]

所以您需要在 TYPE 上使用 std::remove_reference

我认为你对影响是正确的。然而,Kerrek SB在评论中提到了原因:字符串字面值显然是左值而不是右值。因此,在通过转发引用参数传递时,类型被推断为T&而不是T - bweber
当您在代码中使用相同的字符串文字,例如不同位置的“string”,它实际上是相同的字符串。它不是每次都新的临时字符串,而是在程序运行时一直保留在内存中,因此当您引用它时,实际上是通过左值引用引用它。 - xinaiz

0

这可能是编译器的错误。编译器在编译时生成由typeid给出的这些类型对象。编译器不会为每个长度(0到2 ** n)编译数组类型,因此它在需要时进行编译,并且可能会“忘记”重复项。尝试使用一个特殊的模板,其中你将长度与包含的类型分开。这不是类型,但你可以检查它是否等于另一个。

    template<class T, size_t size> 
struct array_t{};

    template <class T, size_t size>
void function(T[size] parameter) {
    std::cout << typeid(array_t<T, size>).name() << " : " << typeid(array_t<char, 5>).name() << std::endl;
    std::cout << std::is_same<array_t<T, size>, array_t<char, 5>>::value << std::endl;
};

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