正如在评论中提到,你可以检测超载operator&
是否可使用SFINAE。和作为Potatoswatter评价所指出的,这些需要是三个独立的检查:
1)是否x.operator&()
被接受
2)operator&(x)
是否被接受
前两个是两种方式用户 - 提供的operator&
可能被定义。
3)是否&x
接受
这第三个检查是必要的,因为x.operator&()
可能会被拒绝,因为operator&
确实存在,但它是私人的。在这种情况下,&x
无效。
这些检查可以通过检查sizeof(f(std::declval<T>()))
来实现,其中f
重载的方式使得返回类型取决于T
是否通过检查。
namespace addressof_helper {
template <typename T>
static char (&checkaddressof(...))[1];
template <typename T>
static char (&checkaddressof(T &&, typename std::remove_reference<decltype(&std::declval<T &>())>::type * = 0))[2];
template <typename T>
static char (&checknonmember(...))[1];
template <typename T>
static char (&checknonmember(T &&, typename std::remove_reference<decltype(operator&(std::declval<T &>()))>::type * = 0))[2];
template <typename T>
static char (&checkmember(...))[1];
template <typename T>
static char (&checkmember(T &&, typename std::remove_reference<decltype(std::declval<T &>().operator&())>::type * = 0))[2];
}
然后,您可以使用这些辅助功能来选择使用其中的addressof
实现:
template <typename T>
constexpr typename std::enable_if<
sizeof(addressof_helper::checkaddressof<T>(std::declval<T>())) == 2
&& sizeof(addressof_helper::checknonmember<T>(std::declval<T>())) == 1
&& sizeof(addressof_helper::checkmember<T>(std::declval<T>())) == 1,
T *>::type addressof(T &t) {
return &t;
}
template <typename T>
/* no constexpr */ typename std::enable_if<
sizeof(addressof_helper::checkaddressof<T>(std::declval<T>())) == 1
|| sizeof(addressof_helper::checknonmember<T>(std::declval<T>())) == 2
|| sizeof(addressof_helper::checkmember<T>(std::declval<T>())) == 2,
T *>::type addressof(T &t) {
return reinterpret_cast<T *>(&const_cast<char &>(reinterpret_cast<const volatile char &>(t)));
}
这使得addressof
在常量表达式,只要operator&
不超载使用。如果它被重载,似乎没有办法可靠地获取可用于常量表达式的形式的地址。
请注意,GCC 4.7拒绝使用此addressof
应用程序的实施案例。海湾合作委员会4.8和更高的工作,铛。
我使用了一个单一实现addressof
转发给我的答案的早期版本中的帮助函数,但我最近意识到这不是一个好主意,因为如果addressof<X>
是很容易导致ODR违规用于多个翻译单元中的一些类X
,其中一些翻译单元中定义了X
,其中一些X
不完整。有两个独立的功能可以避免这个问题。
剩下的唯一问题是如果addressof<X>
在X
的定义operator&
的定义之前在翻译单元中使用,则可能会失败。这应该是非常罕见的,这在实践中不是问题。
测试用例为明智的例子:
class A { } a;
class B { private: B *operator&(); } b;
class C { C *operator&(); } c;
class D { } d;
D *operator&(D &);
extern class E e;
int main() {
constexpr A *pa = addressof(a);
/* no constexpr */ B *pb = addressof(b);
/* no constexpr */ C *pc = addressof(c);
/* no constexpr */ D *pd = addressof(d);
constexpr E *pe = addressof(e);
}
class E { } e;
来源
2013-02-14 19:10:22
hvd
我可以问你为什么需要它? – 2013-02-13 21:35:40
对象的地址不是已知的编译时间。 – Erik 2013-02-13 21:38:50
这是不可能的:足够奇怪的投射不能是'constexpr'。但我无法想象需要“constexpr地址”的情况。 – 2013-02-13 21:39:01