【问题标题】:decltype(*this) bug in VS2013?VS2013 中的 decltype(*this) 错误?
【发布时间】:2014-04-17 19:58:58
【问题描述】:

在尝试制定 C 宏以简化非常量成员函数的编写时,调用具有完全相同逻辑的 const 成员函数(参见 @ 987654321@),我相信我在 VS2013 Update 1 中遇到了 decltype() 错误。

我想使用decltype(*this) 在上述宏中构建static_cast<decltype(*this) const&>(*this) 表达式,以避免宏调用站点传递任何显式类型信息。但是,在 VS2013 中的某些情况下,后一种表达式似乎无法正确添加 const。

下面是一小段代码,我可以将这个 bug 重新生成:

#include <stdio.h>

template<typename DatumT>
struct DynamicArray
{
    DatumT* elements;
    unsigned element_size;
    int count;

    inline const DatumT* operator [](int index) const
    {
        if (index < 0 || index >= count)
            return nullptr;

        return &elements[index];
    }

    inline DatumT* operator [](int index)
    {
#if defined(MAKE_THIS_CODE_WORK)
        DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);
        return const_cast<DatumT*>(_this[index]);
#else
        // warning C4717: 'DynamicArray<int>::operator[]' : recursive on all control paths, function will cause runtime stack overflow
        return const_cast<DatumT*>(
                static_cast<decltype(*this) const>(*this)
                [index]
            );
#endif
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    DynamicArray<int> array = { new int[5], sizeof(int), 5 };
    printf_s("%d", *array[0]);
    delete array.elements;

    return 0;
}

(可能第一个喋喋不休地谈论不使用 std::vector 的人)

您可以编译上面的代码并自己查看警告,或者参考我的唯一评论以了解 VC++ 会向您吐出什么。那你就可以了! defined(MAKE_THIS_CODE_WORK) 表达式让 VC++ 编译代码,就像我除了 #else 代码一样工作。

我在这台机器上没有可靠的 clang 设置,但我可以使用 GCC Explorer 查看 clang 是否抱怨 (click to see/compile code)。它没有。但是,g++ 4.8 会使用相同的代码为您提供‘const’ qualifiers cannot be applied to ‘DynamicArray&amp;’ 错误消息。那么也许g++也有bug?

参考decltype and auto 标准文件(尽管它已经快 11 年了),第 6 页的最底部说非 const 成员函数中的decltype(*this) 应该是T&amp;,所以我很漂亮当然这应该是合法的......

那么我尝试在 *this 上使用 decltype() 并添加 const 是不是错了?或者这是VS2013中的一个错误?显然是 g++ 4.8,但方式不同。

编辑:感谢 Ben Voigt 的回复,我能够弄清楚如何为我想做的事情制作一个独立的 C 宏。

// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...)   \
    const_cast<ret_type>(                               \
        static_cast<                                    \
            std::add_reference<                         \
                std::add_const<                         \
                    std::remove_reference<              \
                        decltype(*this)                 \
                    >::type                             \
                >::type                                 \
            >::type                                     \
        >(*this)                                        \
        __VA_ARGS__                                     \
    )
// We can now implement that operator[] like so:
return CAST_THIS_NONCONST_MEMBER_FUNC(DatumT*, [index]);

最初的愿望是将这一切隐藏在一个宏中,这就是为什么我不想担心创建 typedef 或 this 别名的原因。令人好奇的是 GCC Explorer 中的 clang 没有输出警告……尽管输出程序集确实看起来很可疑。

【问题讨论】:

标签: gcc c++11 visual-studio-2013 decltype


【解决方案1】:

你自己说过,decltype (*this)T&amp;decltype (*this) const &amp; 尝试形成对引用 (T&amp; const &amp;) 的引用。 decltype 触发参考折叠规则 8.3.2p6。但它不会按照你想要的方式折叠。

您可以说decltype(this) const&amp;,但那将是T* const&amp; -- 对 const 指针的引用,而不是指向 const 对象的指针。出于同样的原因,decltype (*this) constconst decltype (*this) 不会形成const T&amp;,而是(T&amp;) const。并且引用上的顶级const 是无用的,因为引用已经禁止重新绑定。

也许你正在寻找类似的东西

const typename remove_reference<decltype(*this)>::type &

但请注意,添加const 时根本不需要演员表。而不是

DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);

只是说

DynamicArray const& _this = *this;

这些结合起来

const typename std::remove_reference<decltype(*this)>::type & this_ = *this;

尽管如此,对于一个非常简单且普遍存在的问题,这仍然是一段愚蠢的代码。直接说:

const auto&amp; this_ = *this;


仅供参考,这是参考折叠规则的文本:

如果 typedef-name (7.1.3, 14.1) 或 decltype-specifier (7.1.6.2) 表示类型 TR 是对类型T,尝试创建类型“对cvTR的左值引用”创建类型“对T的左值引用”,同时尝试创建类型“右值引用”到 cv TR" 创建类型 TR

decltype(*this) 是我们的 decltype 说明符,表示TR,即DynamicArray&lt;DatumT&gt;&amp;。在这里,TDynamicArray&lt;DatumT&gt;。尝试TR const&amp;是第一种情况,尝试创建对(const)TR的左值引用,因此最终结果是T&amp;,而不是const T&amp;。 cv 限定在最内层引用之外。

【讨论】:

  • 谢谢本!我用我试图创建的 C 宏更新了我的问题以隐藏这种废话,同时避免使用 typedef 或 this 别名。我仍然很好奇为什么 GCC Explorer 中的 clang 没有引发错误甚至警告……我回家后必须在适当的环境中尝试一下。
  • @kornman00:在我意识到可以使用auto 之前,此答案的先前版本建议template&lt;typename T&gt; const T&amp; deref_as_const(T* that) { return *that; } 使用它而不是所有这些类型特征垃圾。当存在隐式转换时,尽量不要使用显式转换。如果你的演员阵容错误......好吧,你已经告诉编译器闭嘴并按照它的指示去做。
  • 我在哪里使用显式演员表?我本可以发誓在我发布的所有代码中都坚持使用 static_cast (这是隐式的)和 const_cast (必需的,因为 const 成员函数返回一个 const 对象)。虽然我试图避免将任何细节暴露给宏本身,但 de_ref_as_const 辅助函数也很好
  • 哎呀,正在考虑隐式转换。无论哪种方式,据我所知static_cast 应该只执行合法转换,因为 C 强制转换告诉编译器关闭。
  • @kornman00: static_cast 绝对比reinterpret_cast 或C 风格的强制转换更安全,但仍然默默地执行这里不需要的强制转换,例如调用显式构造函数或向下强制转换。
【解决方案2】:

关于你的宏

// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...)   \
    const_cast<ret_type>(                               \
        static_cast<                                    \
            std::add_reference<                         \
                std::add_const<                         \
                    std::remove_reference<              \
                        decltype(*this)                 \
                    >::type                             \
                >::type                                 \
            >::type                                     \
        >(*this)                                        \
        __VA_ARGS__                                     \
    )

这样做更干净

// Cast [this] to a 'const this&' so that a const member function can be invoked
template<typename T> const T& deref_as_const(T* that) { return *that; }

// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...)   \
    const_cast<ret_type>(deref_as_const(this)__VA_ARGS__)

它更短、独立、与 C++98 兼容(__VA_ARGS__ 除外),并避免了不必要的强制转换

【讨论】:

  • 需要去掉宏中this的解引用,否则可以。虽然,DEBUG 构建将为 deref_as_const 为实例化它的每种类型生成一个函数(更冗长且代码密集的宏不会),即使您声明为inline(至少在 VC++ 中)。
  • 有两个取消引用很好。
猜你喜欢
  • 2018-10-22
  • 1970-01-01
  • 2013-11-30
  • 2015-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多