none
constexprメンバ関数をヘッダとソースへ分割時に発生するリンカエラー

    質問

  • 以下のコードをコンパイルしようとするとLNK1120エラーが発生してしまいました。

    ヘッダに実装も書いた場合は発生しなかったのですが、原因がわからなかったため質問させて頂きました。

    開発環境

    Windows10 Home 64bit

    VisualStudio 2017 Enterprise

    Main.cpp

    #include <stdio.h>
    #include "Test.h"
    
    int main() {
        printf("%d\n", Test(10).geti());
    }

    Test.h

    #pragma once
    
    class Test {
        const int i;
    public:
        explicit constexpr Test(const int n) : i(n) {}
        constexpr int geti() const;
    };

    Test.cpp

    #include "Test.h"
    
    constexpr int Test::geti() const {
        return this->i;
    }




    • 編集済み 108EAA0A 2017年12月20日 9:39
    2017年12月20日 9:01

回答

    • constexpr関数は通常の関数と同様に宣言と定義を分離できます。
    • Main.cppではTest::getiが定義されていないため、非constexprバージョンを呼び出すようにコンパイル(オブジェクトファイルが生成)されています。
    • Test.cppではTest::getiが定義されていますが、どこからも参照されていないため非constexprバージョンの生成を省略しています。
    • リンク時点で非constexprバージョンがどこにも存在しないためご質問のようにリンクエラーが発生します。

    仕様上、Test.cppの時点で非constexprバージョンを生成すべきかどうか、私にはわかりませんでした。しかし、Test.cppで

    static auto p = &Test::geti;

    のように意図的に非constexprバージョンを使用することで非constexprバージョンを生成させることができ、リンクエラーも解消しました。

    とはいえ、説明の通り(おそらく意図せず)非constexprバージョンが使われることになりますので、現実的にはconstexpr関数に関しては宣言と定義を分離しないことをお勧めします。

    • 回答としてマーク 108EAA0A 2017年12月22日 20:25
    2017年12月20日 12:47

すべての返信

    • constexpr関数は通常の関数と同様に宣言と定義を分離できます。
    • Main.cppではTest::getiが定義されていないため、非constexprバージョンを呼び出すようにコンパイル(オブジェクトファイルが生成)されています。
    • Test.cppではTest::getiが定義されていますが、どこからも参照されていないため非constexprバージョンの生成を省略しています。
    • リンク時点で非constexprバージョンがどこにも存在しないためご質問のようにリンクエラーが発生します。

    仕様上、Test.cppの時点で非constexprバージョンを生成すべきかどうか、私にはわかりませんでした。しかし、Test.cppで

    static auto p = &Test::geti;

    のように意図的に非constexprバージョンを使用することで非constexprバージョンを生成させることができ、リンクエラーも解消しました。

    とはいえ、説明の通り(おそらく意図せず)非constexprバージョンが使われることになりますので、現実的にはconstexpr関数に関しては宣言と定義を分離しないことをお勧めします。

    • 回答としてマーク 108EAA0A 2017年12月22日 20:25
    2017年12月20日 12:47
  • そういった仕様があったのですね…

    とてもわかりやすい解説をありがとうございました。

    2017年12月22日 20:24