Perl脳でC++を書く - 純粋仮想メソッド編

純粋仮想メソッドは、Perlのような動的言語ならば特別の工夫はいらず、以下のようにいきなり呼べばいいですね。

#!perl -w
use 5.14.0;
package ScopedReporterBase {
    use Mouse;
    has name => (is => 'ro');
    sub DESTROY {
        my($self) = @_;
        # report()は純粋仮想メソッド
        $self->report();
    }
}
package ScopedReporter {
    use Mouse;
    extends 'ScopedReporterBase';
    # 純粋仮想メソッドを実装する
    sub report {
        my($self) = @_;
        say "Hello, ", $self->name;
    }
}

{
    my $r = ScopedReporter->new(name => 'Foo');
    say "Hey!";
}
__END__
Hey!
Hello, Foo!

これをC++で実現しようとすると一工夫が必要です。既定クラスから呼び出すすべてのメソッドはその時点で見えていなければならないからです。そこで、CRTP: Curiously Recurring Template Patternで静的ポリモーフィズムを実現することで、基底クラスの定義時点で存在していない導出クラスのメソッドを呼ぶことができます*1

#include <iostream>

template <class Derived>
class scoped_reporter_base {
    public:
    scoped_reporter_base(const std::string& name)
        : name_(name) { }

    ~scoped_reporter_base() {
        // Derivedはかならずreport()を用意しなければならないが
        // それはこの時点で宣言が存在しなくても構わない。
        static_cast<Derived*>(this)->report();
    }

    const std::string& name() { return name_; }

    private:
    const std::string name_;
};

class scoped_reporter : public scoped_reporter_base<scoped_reporter> {
    typedef scoped_reporter_base<scoped_reporter> base_class;
    public:
    scoped_reporter(const std::string& name)
        : base_class(name) { }

    // 純粋仮想メソッドを実装する
    void report() {
        std::cout << "Hello, " << this->name() << std::endl;
    }
};

int main() {
    {
        scoped_reporter r("foo");
        std::cout << "Hey!" << std::endl;
    }
    return 0;
}

しかし機能的にはこれで事足りますが、PerlにせよC++にせよ言語のサポートがないのでちょっと分かりにくいですね。

*1:このケースでvirtualキーワードを使うのは正しくないと思います。virtualキーワードを使った動的ポリモーフィズムはまた別のプログラミングパラダイムに基づくものです。