为什么我不能在这个专门的函数模板中将字符串文字传递给 const char* const&
Why I can't pass string literals to const char* const& in this specialized function template
为什么在专用函数模板中将字符串文字传递给 const char* const& 是非法的,而传递给 const char* 是合法的?
事情是这样的。 C++ Primer中有两个关于模板特化的练习:
Exercise 16.63: Define a function template to count the number of
occurrences of a given value in a vector. Test your program by passing it a
vector of doubles, a vector of ints, and a vector of strings.
Exercise 16.64: Write a specialized version of the template from the
previous exercise to handle vector<const char*> and a program that
uses this specialization.
下面的代码是我的答案,当我将字符串文字传递给这个专门的 count 函数时出现编译错误。
// Count the number of occurrences of a given value in a vector
template <typename T>
std::size_t count(const std::vector<T>& vec, const T& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (v == value)
++i;
}
return i;
}
// A specialized version where T = const char*
template <>
std::size_t count(const std::vector<const char*>& vec, const char* const& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (!strcmp(v, value))
++i;
}
return i;
}
int main()
{
std::vector<const char*> sVec{ "cpp", "primer", "cpp", "fifth", "edition", "Cpp", "cpp" };
// Error message: no instance of function template "count" matches the argument list,
// argument types are: (std::vector<const char *, std::allocator<const char *>>, const char [4])
std::cout << count(sVec, "cpp") << std::endl;
return 0;
}
此外,在非模板函数中将字符串文字传递给 const char* const& 是完全可以的,这让我很困惑。
void test(const char* const& str)
{
std::cout << str << std::endl;
}
int main()
{
test("cpp"); // Prints "cpp" as expected
return 0;
}
哦,我把代码改成std::cout << count<const char*>(sVec, "cpp") << std::endl;
然后就一切正常了。似乎编译器将字符串文字“cpp”视为 const char [4] 而不是 const char* const&,所以我必须明确指定模板参数。想知道为什么会这样。
数组和指针是不同的类型。
由于默认转换,您可以将数组作为指针参数传递。
模板就是函数的“模板”。
指定的参数没有匹配的具体函数。所以编译器试图从参数生成一个函数,但无法匹配,所以失败了。
但是当您指定模板参数时,编译器确切地知道您想要哪个函数,生成它,然后尝试使用调用函数的标准规则来调用该函数,这允许它进行转换。
当你有一个模板函数定义时,对函数参数的要求更严格。
在这种情况下,它将尝试匹配数组并从模板生成函数,但失败。所以没有函数可以调用。
你可以专攻:
// A specialized version where T = const char[ArraySize]
template<std::size_t ArraySize>
std::size_t count(const std::vector<const char*>& vec, const char (&value)[ArraySize])
{
return count<const char*>(vec, value);
}
-- 编辑
或者专门定义一个模板参数函数,它定义了一个具体的函数来调用。
您不会“传递给函数模板特化”。该调用是根据原始模板定义推导出来的(不管任何专业化),如果成功则推导出一个类型。然后,如果推导类型存在专门化,则将调用它。
错误消息是关于类型推导失败的。即使您删除了专业化,您也应该会看到完全相同的错误。
类型推导失败,因为T
出现在两个不同的参数中,但每个参数的推导结果不同:
- 根据
vector<const char *>
推导 vector<T>& vec
产生 T=const char *
- 根据
"cpp"
推导 const T& value
产生 T=const char[4]
.
仅当推导的 T
的所有实例都产生相同类型时,推导才会成功。没有通过考虑可用的转换来协调不同类型的额外步骤。
解决该问题的一种方法是使用重载而不是特化(即删除文本 template <>
)。然后是一个由 non-template 函数和推导结果(如果有)组成的重载集。如果推导失败,就像它那样,这不是错误,因为重载集仍然包含一些东西。
为什么在专用函数模板中将字符串文字传递给 const char* const& 是非法的,而传递给 const char* 是合法的?
事情是这样的。 C++ Primer中有两个关于模板特化的练习:
Exercise 16.63: Define a function template to count the number of occurrences of a given value in a vector. Test your program by passing it a vector of doubles, a vector of ints, and a vector of strings.
Exercise 16.64: Write a specialized version of the template from the previous exercise to handle vector<const char*> and a program that uses this specialization.
下面的代码是我的答案,当我将字符串文字传递给这个专门的 count 函数时出现编译错误。
// Count the number of occurrences of a given value in a vector
template <typename T>
std::size_t count(const std::vector<T>& vec, const T& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (v == value)
++i;
}
return i;
}
// A specialized version where T = const char*
template <>
std::size_t count(const std::vector<const char*>& vec, const char* const& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (!strcmp(v, value))
++i;
}
return i;
}
int main()
{
std::vector<const char*> sVec{ "cpp", "primer", "cpp", "fifth", "edition", "Cpp", "cpp" };
// Error message: no instance of function template "count" matches the argument list,
// argument types are: (std::vector<const char *, std::allocator<const char *>>, const char [4])
std::cout << count(sVec, "cpp") << std::endl;
return 0;
}
此外,在非模板函数中将字符串文字传递给 const char* const& 是完全可以的,这让我很困惑。
void test(const char* const& str)
{
std::cout << str << std::endl;
}
int main()
{
test("cpp"); // Prints "cpp" as expected
return 0;
}
哦,我把代码改成std::cout << count<const char*>(sVec, "cpp") << std::endl;
然后就一切正常了。似乎编译器将字符串文字“cpp”视为 const char [4] 而不是 const char* const&,所以我必须明确指定模板参数。想知道为什么会这样。
数组和指针是不同的类型。
由于默认转换,您可以将数组作为指针参数传递。
模板就是函数的“模板”。
指定的参数没有匹配的具体函数。所以编译器试图从参数生成一个函数,但无法匹配,所以失败了。
但是当您指定模板参数时,编译器确切地知道您想要哪个函数,生成它,然后尝试使用调用函数的标准规则来调用该函数,这允许它进行转换。
当你有一个模板函数定义时,对函数参数的要求更严格。
在这种情况下,它将尝试匹配数组并从模板生成函数,但失败。所以没有函数可以调用。
你可以专攻:
// A specialized version where T = const char[ArraySize]
template<std::size_t ArraySize>
std::size_t count(const std::vector<const char*>& vec, const char (&value)[ArraySize])
{
return count<const char*>(vec, value);
}
-- 编辑
或者专门定义一个模板参数函数,它定义了一个具体的函数来调用。
您不会“传递给函数模板特化”。该调用是根据原始模板定义推导出来的(不管任何专业化),如果成功则推导出一个类型。然后,如果推导类型存在专门化,则将调用它。
错误消息是关于类型推导失败的。即使您删除了专业化,您也应该会看到完全相同的错误。
类型推导失败,因为T
出现在两个不同的参数中,但每个参数的推导结果不同:
- 根据
vector<const char *>
推导vector<T>& vec
产生T=const char *
- 根据
"cpp"
推导const T& value
产生T=const char[4]
.
仅当推导的 T
的所有实例都产生相同类型时,推导才会成功。没有通过考虑可用的转换来协调不同类型的额外步骤。
解决该问题的一种方法是使用重载而不是特化(即删除文本 template <>
)。然后是一个由 non-template 函数和推导结果(如果有)组成的重载集。如果推导失败,就像它那样,这不是错误,因为重载集仍然包含一些东西。