当我试图制定一个C宏来简化调用具有完全相同逻辑的常量成员函数的非常数成员函数的编写时(请参阅有效C++中的第1章第3项"避免常量和非常数成员功能中的重复"),我相信我在VS2013 Update 1中遇到了decltype()
错误。
我想在前面提到的宏中使用decltype(*this)
来构建static_cast<decltype(*this) const&>(*this)
表达式,以避免宏调用站点传递任何显式类型信息。然而,在VS2013中,后一个表达式在某些情况下似乎无法正确添加常量。
以下是我能够使repo修复错误的一小段代码:
#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++会向你吐出什么。那你就可以了!CCD_ 4表达式让VC++编译代码,就像我排除CCD_。
我在这台机器上没有可靠的clang设置,但我可以使用GCC资源管理器查看clang是否抱怨(点击查看/编译代码)。但事实并非如此。但是,g++4.8将使用相同的代码向您提供‘const’ qualifiers cannot be applied to ‘DynamicArray&’
错误消息。那么也许g++也有一个bug?
参考decltype和auto标准的论文(尽管它已经有11年的历史了),第6页的最底部说,非常量成员函数中的decltype(*this)
应该是T&
,所以我很确定这应该是合法的。。。
那么,我试图在*this上使用decltype()并添加const,这是错的吗?或者这是VS2013中的一个错误?显然g++4.8,但方式不同。
edit:多亏了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没有输出警告。。。尽管输出组件看起来确实可疑。
你自己说过,decltype (*this)
就是T&
。CCD_ 12试图形成对引用(T& const &
)的引用。decltype
触发参考折叠规则8.3.2p6。但它不会以你想要的方式崩溃。
你可以说decltype(this) const&
,但那应该是T* const&
——对常量指针的引用,而不是对常量对象的指针。出于同样的原因,decltype (*this) const
和const decltype (*this)
不形成const T&
,而是形成(T&) 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& this_ = *this;
仅供参考,以下是参考折叠规则的文本:
如果typedef名称(7.1.3,14.1)或decltype说明符表示类型
TR
是对类型T
的引用,则尝试创建类型"对cvTR
的左值引用"将创建类型"对于T
的左值参考",而尝试创建类型"对cvTR
的右值参考"将创建类型TR
。
decltype(*this)
是我们的decltype说明符,它表示TR
,即DynamicArray<DatumT>&
。这里,T
是DynamicArray<DatumT>
。尝试TR const&
是第一种情况,尝试创建对(const)TR
的左值引用,因此最终结果是T&
,而不是const T&
。简历资格不在最重要的参考范围之内。
关于宏
// 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__)
它更短,自包含,除了__VA_ARGS__
之外,与C++98兼容,并避免了不必要的强制转换