Clangにopaque-enum-declarationがコミットされた。これで、残す機能はあと2つ、AttributeとInheriting constructorsだ。おそらく、最後に残るのはInheriting constructorsになるだろう。なぜか。誰も興味がないからだ。
freenodeでDouglas Gregorとチャットしていてこれを聞いたのだが、Inheriting constructorsが未だに存在しない理由は、興味を持つ者がほとんどいないからだそうである。それに、Inheriting constructorsの文面には、未だに曖昧な部分もあるとか。
実は、Inheriting constructorsとほぼ同等機能を、Variadic Templatesによってエミュレートできるのだ。これは、Douglas Gregorもペーパーで書いている。
US-65: Removing Inheriting Constructors
template<typename Mixin> class add_color : public Mixin { Color color; public: template<typename ...Args> add_color(Args &&...args) : Mixin(std::forward<Args>(args)...), color(Red) { } };
この例では、テンプレートパラメーターであるMixinがどのようなコンストラクターを持つのかは、インスタンス化されるまでわからない。そのため、このMixinを基本クラスに据えようと思ったら、Inheriting constructorsが必要である。あるいは、エミュレーションだ。
std::forwardを使っているのは重要である。詳しくは、Perfect Forwardingなどのキーワードで調べること。
さて、このVariadic Teamplatesは正しく動くが、2つ問題がある。まずひとつは、Variadic Templatesなので、どんな実引数に対してもインスタンス化されてしまうことだ。エラーメッセージは、クラスadd_colorがオーバーロード解決可能なコンストラクターを持っていないというわかりやすいものではなく、テンプレートパラメーターMixinのコンストラクターがオーバーロード解決できないというものになってしまう。正しくコンパイルエラーにはなるものの、少しわかりにくい。
もうひとつの問題は、explicitやconstexprが引き継がれないことだ。たとえ基本クラスのexplicitコンストラクターであっても、派生クラスのコンストラクターはexplicitではないので、explicitの挙動はしない。さらに、constexprでもなくなってしまう。
しかし、この2つの問題は、些細なものである。Variadic Templatesによるエミュレーションは、十分実用的だ。
SFINAE を使わない Perfect Forward は, copy 時に予期せぬ動作を引き起こすので,使うべきで無いのでは?
ReplyDeletehttp://ideone.com/jM9VA
暗黙のコピーコンストラクタはconst lvalue referenceでしたね。
ReplyDeleteしかし、これは闇雲に何でもデータメンバーに突っ込んでいるから起こる問題ではないですかね。
ここでの問題は、基本クラスのコンストラクターに、いわば処理をデリゲートするものでしょう。
違います. オーバーロード解決によるものなので,一つの引数を T&& で推論させる場合(および Variadic Templates の場合)には常に起こる問題ですね. これを避けるには SFINAE を使うか const でない lvalue reference を取る ctor を明示的に定義する必要があるはずです.
ReplyDelete蛇足: 関数の内部で行われる処理は,呼び出される関数の検索には一切関係ないので, ctor の中で何をやっていても関係ない
ReplyDeleteコンストラクターのデリゲートでコードの重複は防げますね。
ReplyDeleteまあ、なんにせよネイティブなInheriting Constructorか、全て手動なコンストラクター群には勝てませんが。