我正在玩c ++ 14中的重载运算符,我尝试匹配两种类型的参数:any-old-const-char *和a-string-literal。
也就是说,我试图看看我是否可以区别对待:
const char * run_time;
和
"compile time"
我编写了下面的代码,如图所示,当我尝试span >> "literal"
时,它调用了const char*
函数。
当我#if 0
- const char*
版本时,模板版本被调用就好了。
如果我更改模板版本以获取literal
的rvalue-reference(&&)参数,则无法编译。
如果我添加const char (&literal)[]
非模板版本,则仍然首选const char*
版本。删除const-char *版本,首选模板版本。
你能解释一下吗?特别是:
const char*
优先于const char (&)[N]
?const char (&)[N]
优先于const char (&)[]
(非模板)?const char (&&)[N]
无法编译?感谢。
#include <iostream>
using namespace std;
#include <gsl/gsl>
#include <type_name.h++>
template<unsigned N>
auto
operator>>(gsl::span<const char*,-1>& spn, const char (&literal)[N])
-> gsl::span<const char*, -1>&
{
cout << "Got array: " << literal << endl;
return spn;
}
auto
operator>>(gsl::span<const char*,-1>& spn, const char *literal)
-> gsl::span<const char*, -1>&
{
cout << "Got const-char*: " << literal << endl;
return spn;
}
#if 0
#endif
int
main(int argc, const char *argv[])
{
auto spn = gsl::span<const char*>(argv, argc);
cout << type_name<decltype(spn)>() << endl; // gsl::span<const char *, -1>
cout << type_name<decltype("literal")>() << endl; // char const (&)[8]
cout << type_name<decltype(("literal"))>() << endl; // char const (&)[8]
auto helpx = "literal";
cout << type_name<decltype(helpx)>() << endl; // const char *
spn >> "literal"; // Got const-char*: literal
return 0;
}
修改
如果重要,我会编译:
c++ --std=c++14 -Iinclude -c -o main.o main.c++
c ++说:
$ c++ --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
答案 0 :(得分:9)
为什么
const char*
优先于const char (&)[N]
?
其原因相当技术性。即使从const char[N]
到const char*
的字符串文字的衰减是转换,它也属于“左值变换”类别,因此被[over.ics.rank] / 3认为是很好,没有任何转换。由于任何一个重载都需要“无转换”,因此非模板重载会获胜。
为什么
const char (&)[N]
优先于const char (&)[]
(非模板)?
无法将对未知绑定数组的引用绑定到类型已知绑定数组的值。相反,对未知边界数组的引用只能绑定到本身是未知边界数组的值。
为什么
const char (&&)[N]
无法编译?
字符串文字是一个左值,所以我不确定你为什么会这样做。
是否有“正确的方法”来捕获文字字符串?
您可以使用辅助函数模板,使用转发引用捕获其参数,以便不破坏任何类型信息(const char*
与const char[N]
),然后使用模板特化分派类型。如果传入const char*
或const char[N]
以外的任何内容,您可能还希望使用SFINAE来确保它被禁用。即,
template <bool b>
struct f_helper;
template <>
struct f_helper<true> {
void do_it(const char*) {
puts("pointer");
}
};
template <>
struct f_helper<false> {
template <std::size_t N>
void do_it(const char (&)[N]) {
printf("array of length %zd\n", N);
}
};
template <class T, class = typename std::enable_if<std::is_same<char*, std::decay_t<T>>::value ||
std::is_same<const char*, std::decay_t<T>>::value>::type>
void f(T&& s) {
f_helper<std::is_pointer<std::remove_reference_t<T>>::value>{}.do_it(s);
}
Coliru链接:http://coliru.stacked-crooked.com/a/0e9681868d715e87
答案 1 :(得分:1)
13.3.3最佳可行功能[over.match.best]
...
鉴于这些定义,可行函数F1被定义为比另一个可行函数更好的函数 F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后
...
(1.7) F1不是函数模板特化,F2是函数模板特化
实际上非模板const char (&)[]
似乎根本不编译,因为它是对非绑定数组的引用。可以传递像const char []
这样的指针,但不能传递数组。
至少出于与(2)
template< typename = void > void
foo(char const * & text)
{
::std::cout << "got ptr" << ::std::endl;
}
请注意,提供另一个使用指针的模板不会起作用,因为两个模板特化都很好,我们会选择过多的函数。