質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.87%

  • C++

    3116questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C++:template <...> static constexpr なラムダ式でコンパイルエラーが発生する

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 253

pute

score 87

[修正] 「ジェネリックラムダ」と書いていたが、ジェネリックラムダは別のなにかだったのでタイトル等を変更

 前提・実現したいこと

C++ で遊んでいます。
クラスの static かつ非型テンプレートなメンバにラムダ式を持ったときに以下のエラーが出ました。
テンプレートは実体化された型や値ごとに別の名前を与えられると思っていたのですが、
実は違うのでしょうか?
また、解決策としてはどのようなものがあるでしょうか?

※やりたいこと:
・非型テンプレートでラムダを定義したい
・それをあるクラスが static 変数としてもつようにしたい

 発生している問題・エラーメッセージ

シンボルの重複が報告されます。

$ g++ -std=c++17 a.cpp

C:\msys64\tmp\ccKmfXl2.s: Assembler messages:
C:\msys64\tmp\ccKmfXl2.s:26: Error: symbol `_ZNK1AUlvE_clEv' is already defined

 該当のソースコード

// a.cpp
#include <iostream>

struct A {
    template <int N>
    static constexpr inline auto X = []() { return N; };

    template <int N>
    static constexpr inline int Y = N;
};

int main() {
    std::cout << A::X<1>() << std::endl; // ← ここをコメントアウトするとエラーは発生しなくなる
    std::cout << A::X<2>() << std::endl;
    std::cout << A::Y<1> << std::endl;
    std::cout << A::Y<2> << std::endl;
    return 0;
}

 試したこと

clang++ でコンパイルするとエラーにはなりませんでしたが、期待する出力にはなりませんでした。

$ clang++ -std=c++17 && ./a

1
1
1
2
// 期待する出力:
// 1
// 2
// 1
// 2

 補足情報(FW/ツールのバージョンなど)

$ g++ --version

g++.exe (Rev2, Built by MSYS2 project) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang++ --version

clang version 5.0.1 (tags/RELEASE_501/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\msys64\mingw64\bin
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

こんにちは。

まるでラムダ式に名前が付いているような挙動ですね。
clangもバージョンによって挙動が異なるようです。5.0.0まではおっしゃるような結果ですが、6.0.0以降なら期待通りの結果でした。

staticとinlineが被るような使い方ですし、変数テンプレートも機能としては新しいですし、更にconstexprラムダも新しかったと思います。バグっていても仕方ないかも?


あっと、そのラムダ式はジェネリック・ラムダではないと思います。引数がありませんから。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/12 23:47

    ご回答ありがとうございます。
    ジェネリックラムダについてはその通りでした。こそっと修正。
    コンパイラの実装の問題であれば、私には手出しできません……。
    なにかこう、おしゃれな感じの解決策はあるでしょうか?

    キャンセル

  • 2018/05/13 00:10

    関数テンプレートで十分かもです。
    template <int N>
    static constexpr auto X() { return N; }

    キャンセル

  • 2018/05/13 00:29

    うーん、そうします。
    ありがとうございます。

    キャンセル

  • 2018/05/15 13:01

    FYI: 「_ZNK1AUlvE_clEv」をデマングルすると「A::{lambda()#1}::operator()() const」となります。(via https://demangler.com/) なので、コンパイルエラーは「ラムダ式から生成されるクロージャクラス定義が重複している」と言っていますね。

    雰囲気解釈ですが、ここではODR違反が生じているように思えます。template <int N> static constexpr inline auto X = []() { ... }; のautoで推論されるクロージャクラスの型情報には、非型テンプレートパラメータNが登場しませんから、A::X<1>とA::X<2>の型を区別できません。たぶん。

    解決策はChironianさんコメントにあるとおり関数テンプレートを用いるか、最初からYのように変数テンプレートを利用するべきかと思います。

    キャンセル

  • 2018/05/15 14:43

    yohhoyさん。
    なるほど。
    実体化される際には異なるクラスのオブジェクトになる筈だから{lambda()#1}が可笑しい感じがしますね。
    ↓確かめてみました。
    https://wandbox.org/permlink/Szlq37bPqI0DmANJ

    clang 7.0.0ならちゃんと名前が異なってます。
    clangの変遷を見るとコンパイラ実装者の苦労が忍ばれます。

    しかし、ちゃんと通るのはclang 7.0.0だけでした。古いclangでもTYPENAMEでデマングルしなければ通ります。(TYPENAMEになにかミスがあるかも知れない。)

    キャンセル

  • 2018/05/15 15:11

    TYPENAMEマクロの問題ではなく、abi::__cxa_demangle関数側が追いついていないんだと思います。

    手動デマングル(しなくとも出力シンボルを比較)する限りでは、Clang 5.0.0以前とGCCではA::X<N>のNが型情報に含まれず、Clang 6.0.0以降ではNも含めた型情報として扱うようですね。どちらの振る舞いが正しいのかは謎です...

    キャンセル

  • 2018/05/15 16:00

    > abi::__cxa_demangle関数側が追いついていないんだと思います。
    私もどちらかというとその可能性が高そうな気がします。

    > どちらの振る舞いが正しいのかは謎です...
    なるほど。規格書で規定していない可能性も高そうですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.87%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3116questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。