【发布时间】:2017-01-21 11:49:27
【问题描述】:
我正在编写一个类似于std::function 的类型擦除函数包装器。 (是的,我见过类似的实现,甚至是p0288r0 提案,但我的用例非常狭窄而且有些专业。)。下面高度简化的代码说明了我当前的实现:
class Func{
alignas(sizeof(void*)) char c[64]; //align to word boundary
struct base{
virtual void operator()() = 0;
virtual ~base(){}
};
template<typename T> struct derived : public base{
derived(T&& t) : callable(std::move(t)) {}
void operator()() override{ callable(); }
T callable;
};
public:
Func() = delete;
Func(const Func&) = delete;
template<typename F> //SFINAE constraints skipped for brevity
Func(F&& f){
static_assert(sizeof(derived<F>) <= sizeof(c), "");
new(c) derived<F>(std::forward<F>(f));
}
void operator () (){
return reinterpret_cast<base*>(c)->operator()(); //Warning
}
~Func(){
reinterpret_cast<base*>(c)->~base(); //Warning
}
};
Compiled,GCC 6.1 警告strict-aliasing:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
return reinterpret_cast<T*>(c)->operator()();
我也知道strict-aliasing rule。另一方面,我目前不知道利用小对象堆栈优化的更好方法。尽管有警告,但我所有的测试都通过了 GCC 和 Clang,(并且额外的间接级别阻止了 GCC 的警告)。我的问题是:
- 我最终会在这种情况下无视警告而被烧死吗?
- 有没有更好的就地对象创建方法?
查看完整示例:Live on Coliru
【问题讨论】:
-
忽略警告,您正在使用 UB 以及 现在 与您的 current 编译器(版本)可能一起工作的内容i> 将来使用不同的编译器或当前编译器的不同版本随机中断。忽略 UB 后果自负。它最终会咬人。
-
@JesperJuhl,警告并不总是完美的,编译器经常抱怨与给定代码库无关的问题。我不是严格混叠专家,但可能有一个完全符合标准的解决方案仍然会产生这些警告
-
@AaronMcDaid,不,这是违规行为,一清二楚。
-
一个教育性的解释,也许是完全成熟的答案,会有所帮助
-
@AaronMcDaid,因为您只能在有限的情况下通过不同类型的指针访问对象。这些都不适用于这里。