为什么``literal``鼓励在C++参数类型匹配中衰减为`const char*`?

agh*_*ast 13 c++ c++14

我正在玩c ++ 14中的重载运算符,我尝试匹配两种类型的参数:any-old-const-char*和a-string-literal.

也就是说,我试图看看我是否可以区分:

const char * run_time;
Run Code Online (Sandbox Code Playgroud)

"compile time"
Run Code Online (Sandbox Code Playgroud)

我编写了下面的代码,如图所示,当我尝试span >> "literal"它时调用了该const char*函数.

当我#if 0退房手续的const char*版本,模板版本被调用就好了.

如果我更改模板版本以获取rvalue-reference(&&)参数literal,则不会编译.

如果我添加const char (&literal)[]非模板版本,则该const char*版本仍然是首选.删除const-char*版本,首选模板版本.

你能解释一下吗?特别是:

  1. 为什么const char*首选const char (&)[N]
  2. 为什么const char (&)[N]首选const char (&)[](非模板)?
  3. 为什么const char (&&)[N]无法编译?
  4. 有没有"正确的方法"来捕获文字字符串?

谢谢.

#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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

万一重要,我正在编译:

c++ --std=c++14 -Iinclude   -c -o main.o main.c++
Run Code Online (Sandbox Code Playgroud)

而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
Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 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]),然后使用模板特型派遣.您可能还想使用SFINAE来确保在传入除了const char*const char[N]传入以外的任何内容时禁用它.

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);
}
Run Code Online (Sandbox Code Playgroud)

Coliru链接:http://coliru.stacked-crooked.com/a/0e9681868d715e87