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

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

新規登録して質問してみよう
ただいま回答率
85.51%
C++

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

Q&A

解決済

1回答

2401閲覧

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

pute

総合スコア134

C++

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

1グッド

1クリップ

投稿2018/05/12 14:17

編集2018/05/12 14:35

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

前提・実現したいこと

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

該当のソースコード

C++

1// a.cpp 2#include <iostream> 3 4struct A { 5 template <int N> 6 static constexpr inline auto X = []() { return N; }; 7 8 template <int N> 9 static constexpr inline int Y = N; 10}; 11 12int main() { 13 std::cout << A::X<1>() << std::endl; // ← ここをコメントアウトするとエラーは発生しなくなる 14 std::cout << A::X<2>() << std::endl; 15 std::cout << A::Y<1> << std::endl; 16 std::cout << A::Y<2> << std::endl; 17 return 0; 18}

試したこと

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
yohhoy👍を押しています

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんにちは。

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

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


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

投稿2018/05/12 14:40

編集2018/05/12 14:42
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pute

2018/05/12 14:47

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

2018/05/12 15:10

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

2018/05/12 15:29

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

2018/05/15 04: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のように変数テンプレートを利用するべきかと思います。
Chironian

2018/05/15 05:43

yohhoyさん。 なるほど。 実体化される際には異なるクラスのオブジェクトになる筈だから{lambda()#1}が可笑しい感じがしますね。 ↓確かめてみました。 https://wandbox.org/permlink/Szlq37bPqI0DmANJ clang 7.0.0ならちゃんと名前が異なってます。 clangの変遷を見るとコンパイラ実装者の苦労が忍ばれます。 しかし、ちゃんと通るのはclang 7.0.0だけでした。古いclangでもTYPENAMEでデマングルしなければ通ります。(TYPENAMEになにかミスがあるかも知れない。)
yohhoy

2018/05/15 06:11

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

2018/05/15 07:00

> abi::__cxa_demangle関数側が追いついていないんだと思います。 私もどちらかというとその可能性が高そうな気がします。 > どちらの振る舞いが正しいのかは謎です... なるほど。規格書で規定していない可能性も高そうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問