【问题标题】:array decay to pointer and overload resolution数组衰减到指针和重载分辨率
【发布时间】:2014-02-23 18:36:19
【问题描述】:

我希望能够在重载解析中区分数组和指针:

class string {
public:
        string(const char* c_str);

        template<int N>
        string(const char (&str) [N]);
};


int main() {
        const char* c_str = "foo";
        string foo(c_str);      // ok will call string(const char*)

        string bar("bar");      // call string(const char*) instead of the array version
}

到目前为止我发现的最好的方法是使用对指针的引用而不是指针:

class string {
public:
        string(const char*& c_str);

        template<int N>
        string(const char (&str) [N]);
};


int main() {
        const char* c_str = "foo";
        string foo(c_str);      // ok will call string(const char*)
        string bar("bar");      // ok, will call the array version
}

这不完全一样,我想知道是否存在更好的方法

【问题讨论】:

  • 您为什么期望调用之间存在差异,以及您打算使用这种(不存在的)副作用是什么?顺便说一句,在专用命名空间之外命名一个类 string 会带来麻烦。
  • 我预计会有所不同,因为当我调用 string bar("bar") 时,我不传递指针而是传递数组,这就是指向指针衰减的数组开始,我想获得数组的大小在编译时,如果可用,memcpy 远好于strcpy。否则,此类具有正确的命名空间 ofc,为了清楚起见,我将其删除。
  • 如果数组是静态的我可以,尝试第二个sn-p代码,你会看到,带有模板数组参数的构造函数在第二次调用(string bar("bar"))时被调用。我想要的是一种更好的方法来区分数组中的指针。
  • init 函数可以吗?我不确定如何使用构造函数。
  • 使用构造函数会更好,但知道如何做总是好的:)

标签: c++ arrays pointers


【解决方案1】:

当两者都可行时,您需要使第一个重载成为较差的选择。目前它们在转化排名上处于平局(两者都是“完全匹配”),然后由于首选非模板而打破平局。

这应该会使转化排名变差:

struct stg
{
    struct cvt { const char* p; cvt(const char* p_p) : p(p_p) {} };

    // matches const char*, but disfavored in overload ranking
    stg(cvt c_str); // use c_str.p inside :(  Or add an implicit conversion

    template<int N>
    stg(const char (&str) [N]);
};

【讨论】:

  • 谢谢,我不知道为什么我忘记了这个选项,它更具可读性。
【解决方案2】:

您可以使用 SFINAE。这可能不是最好的方法,但应该可以:

//thanks to dyp for further reduction
template<typename T, typename = typename std::enable_if<std::is_same<T, char>::value>::type>
string(const T * const &) {std::cout << "const char *\n";}

template<std::size_t N> //credit to jrok for noticing the unnecessary SFINAE
string(const char(&)[N]) {std::cout << "const char(&)[" << N << "]\n";}

这是live example。

【讨论】:

  • 确实它会做与传递指针引用完全相同的事情,因为友元函数的签名是string make_string(const T &amp;)。也许这是唯一的方法,我正在尝试使用 SFINAE,但到目前为止我还没有结果。谢谢你的回答
  • @pan-,我已经更新了使用 SFINAE 的答案,因为它正在做同样的事情,而我之前没有意识到这一点。现在还有另一个更新允许char *。
  • 回复后看到更新了,好多了,可以在非指针引用的情况下工作:coliru.stacked-crooked.com/a/1c8faf4816d1b84c非常感谢
  • @pan-,啊,好点子。如果您也希望这样做char *,请确保您获得更新的代码。我不知道有什么比单独检查更好的方法来添加它,因为const 隐藏在指针后面。也许std::remove_pointer,但是你必须检查它是否是一个开始的指针才能让它工作。
【解决方案3】:

可以检测到此问题的更通用版本,如下所示。

template <class T>
void func(T, 
          typename std::enable_if<std::is_pointer<T>::value, void>::type * = 0)
{
  // catch ptr
}

template <class T, int N>
void func(T (&)[N])
{
  //catch array
}

int main(void)
{
  int arr[5];
  char *b = 0;
  func(arr); // catch array
  func(b);   // catch ptr
}

【讨论】:

    猜你喜欢
    • 2011-11-14
    • 2021-05-06
    • 2021-12-01
    相关资源
    最近更新 更多