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

时间:2017-05-02 07:10:33

标签: c++ c++14

我正在玩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 *版本,首选模板版本。

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

  1. 为什么const char*优先于const char (&)[N]
  2. 为什么const char (&)[N]优先于const char (&)[](非模板)?
  3. 为什么const char (&&)[N]无法编译?
  4. 是否有正确的方式"捕获文字字符串?
  5. 感谢。

    #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
    

2 个答案:

答案 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)

  1. 带有指针的重载是优先的,因为根据
  2. 它不是模板
      

    13.3.3最佳可行功能[over.match.best]

         

    ...

         

    鉴于这些定义,可行函数F1被定义为比另一个可行函数更好的函数   F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后

         

    ...

         

    (1.7)   F1不是函数模板特化,F2是函数模板特化

    1. 实际上非模板const char (&)[]似乎根本不编译,因为它是对非绑定数组的引用。可以传递像const char []这样的指针,但不能传递数组。

    2. 至少出于与(2)

    3. 相同的原因,这应该失败
    4. 您可以提供另一个参考指针的模板:
    5. template< typename = void > void
      foo(char const * & text)
      {
          ::std::cout << "got ptr" << ::std::endl;
      }
      

      请注意,提供另一个使用指针的模板不会起作用,因为两个模板特化都很好,我们会选择过多的函数。