2012-03-28

clangにinheriting constructorが実装されない理由

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によるエミュレーションは、十分実用的だ。

5 comments:

SubaruG said...

SFINAE を使わない Perfect Forward は, copy 時に予期せぬ動作を引き起こすので,使うべきで無いのでは?
http://ideone.com/jM9VA

江添亮 said...

暗黙のコピーコンストラクタはconst lvalue referenceでしたね。

しかし、これは闇雲に何でもデータメンバーに突っ込んでいるから起こる問題ではないですかね。

ここでの問題は、基本クラスのコンストラクターに、いわば処理をデリゲートするものでしょう。

SubaruG said...

違います. オーバーロード解決によるものなので,一つの引数を T&& で推論させる場合(および Variadic Templates の場合)には常に起こる問題ですね. これを避けるには SFINAE を使うか const でない lvalue reference を取る ctor を明示的に定義する必要があるはずです.

SubaruG said...

蛇足: 関数の内部で行われる処理は,呼び出される関数の検索には一切関係ないので, ctor の中で何をやっていても関係ない

江添亮 said...

コンストラクターのデリゲートでコードの重複は防げますね。

まあ、なんにせよネイティブなInheriting Constructorか、全て手動なコンストラクター群には勝てませんが。