尝试制定一个C宏以简化非const成员函数的编写,调用具有完全相同逻辑的const成员函数(参见{第1章,第3章“避免const和非常量成员函数中的重复”) {3}}),我相信我在VS2013 Update 1中遇到了decltype()
错误。
我想使用decltype(*this)
在上述宏中构建static_cast<decltype(*this) const&>(*this)
表达式,以避免宏调用站点传递任何显式类型信息。但是,在VS2013的某些情况下,后一个表达式似乎没有正确地添加const。
这是我能够回复错误的一小段代码:
#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 ++会向您发出什么。那你可以!要使VC ++编译代码的defined(MAKE_THIS_CODE_WORK)
表达式,除了#else
代码之外的其他代码。
我没有在这台机器上设置我可信赖的clang,但是我能够使用GCC Explorer查看clang是否抱怨(Effective C++)。它没有。但是,g ++ 4.8将使用相同的代码为您提供‘const’ qualifiers cannot be applied to ‘DynamicArray&’
错误消息。那么也许g ++也有一个bug?
参考click to see/compile code标准文件(虽然它已有近11年历史),第6页的最底部说非{const}成员函数中的decltype(*this)
应为T&
所以我很确定这应该是合法的......
所以我错误地尝试在*上使用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没有输出警告......尽管输出组件确实显得很腥。
答案 0 :(得分:5)
你自己说,decltype (*this)
是T&
。 decltype (*this) const &
尝试形成对引用的引用(T& const &
)。 decltype
触发参考折叠规则8.3.2p6。但它并没有像你想的那样崩溃。
你可以说decltype(this) const&
,但那是T* const&
- 对const指针的引用,而不是指向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-name (7.1.3,14.1)或 decltype-specifier (7.1.6.2)表示类型
TR
是对类型T
的引用,尝试创建类型“对 cvTR
的左值引用”会创建类型“对T
的左值引用”,而尝试创建类型“对 cvTR
的右值引用”会创建类型TR
。
decltype(*this)
是我们的decltype-specifier,表示TR
,即DynamicArray<DatumT>&
。在此,T
为DynamicArray<DatumT>
。尝试TR const&
是第一种情况,尝试创建对(const)TR
的左值引用,因此最终结果为T&
,而不是const T&
。 cv资格不在最内层的参考范围内。
答案 1 :(得分:0)
关于你的宏
// 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__)
它更短,自包含,与{+ 1}}兼容,与C ++ 98兼容,并避免不必要的演员