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

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

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

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

Q&A

解決済

2回答

1208閲覧

C++ 定数式について

jbe00214

総合スコア63

C++

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

0グッド

2クリップ

投稿2022/09/20 03:58

編集2022/09/20 10:16

前提

定数式を理解していたつもりですが,解決できない事例が出てきました。
以下の bar関数の#1 のコードのように ar を参照にしないとエラーは出ないのに,#2 のコードのように,参照をつけるとコンパイルできません。これはどのような理由によるものなのでしょうか。調べても分かりませんでした。
また,foo関数をどのように変更するとコンパイルできるのでしょうか。

実現したいこと

以下のコードの#2のように左辺値参照のまま,fooに渡したい。右辺値の参照でもよい。

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

Constexpr variable 'D' must be initialized by a constant expression function parameter 'ar' with unknown value cannot be used in a constant expression

該当のソースコード

C++

1#include<array> 2#include<iostream> 3 4template<typename T> 5constexpr std::size_t foo( T&& ar ) { 6 return ar.size(); 7} 8 9template<typename T> 10void bar(T ar){ //#1 11//void bar(const T& ar){ //#2 エラーとなる 12//void bar( T&& ar ){ //#2 エラーとなる 13 constexpr std::size_t D = foo(ar); 14 std::cout << D <<'\n'; 15} 16 17int main (){ 18 std::array<int,3> ar{1,2,3}; 19 bar(ar); 20}

試したこと

以下のようにfooの引数arの参照を外してみた。bar( T ar )でもコンパイルできませんでした。
エラー内容
constexpr std::size_t D = foo(ar);の行において
function parameter 'ar' with unknown value cannot be used in a constant expression

c++

1 2template<typename T> 3constexpr 4std::size_t 5foo( T ar ) {///参照を外した 6 return ar.size(); 7}

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

clang
C++17

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

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

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

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

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

guest

回答2

0

ベストアンサー

定数式に存在してはいけないものとして定義されている中に「参照型の参照」があるのを見つけました。 複雑な例外もあるようですが、質問の例の場合はいずれにもあてはまりません。

ですから constexpr 変数の初期化式に (参照であるような) ar が (sizeof のオペランドなどの評価されない文脈を除いて) 現れること自体が制限にひっかかってしまいます。

初期化式の制限として引っかかっているので foo の定義の側で出来ることはないと思います。

追記

質問中の「試したこと」のコードについては説明が簡単です。 「定数ではない」というごく単純なことです。

参照型を通じてメンバ関数を呼出すことは定数式の定義に反しません。 また、オブジェクトが定数でない場合でも constexpr 指定されたメンバ関数の定義が適切であれば定数式になります。

cpp

1#include<array> 2#include<iostream> 3 4int main (){ 5 std::array<int,3> ar{1,2,3}; 6 constexpr auto foo = ar.size(); // ar は定数ではないがこの式は定数式 7}

このとき sizear の内容に依存しないのでコンパイル時に確定できます。

一方で constexpr 指定された関数は実引数が定数式であることが要求されます。 コンパイル時に呼び出されるのでコンパイル時にわからないものを渡すことは出来ません。

つまり以下のような定義は (たとえ引数を使わなくても) エラーになってしまいます。

cpp

1constexpr int foo(int x) { 2 return 1; 3} 4 5int main (){ 6 int a = 1; 7 constexpr auto bar = foo(a); 8}

しかし変数の場所は事前にわかるので参照は定数として機能します。

cpp

1constexpr int foo(const int& x) { 2 return 1; 3} 4 5int main (){ 6 int a = 1; 7 constexpr auto bar = foo(a); 8}

投稿2022/09/20 09:00

編集2022/09/20 10:50
SaitoAtsushi

総合スコア5446

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

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

jbe00214

2022/09/20 10:07

いつもありがとうございます。「定数式には参照の参照があってはいけない」ので,参照として渡されたbar内のarを経由して,fooの ar がさらに参照で受け取った上で, ar.size()とすることはできません,という意味ですね。誤解していたらすいません。そのように理解した上で,「試したこと」(追加しました)のようにfooに参照を使わないでみましたが,今度は,barがT arのままでもコンパイルできませんでした。謎が深まっています。
jbe00214

2022/09/20 11:44

よく分かりました。つまり,foo では参照で受け取らねばならず,一方,bar は参照であってはいけない,ということになるのですね。ご丁寧なご説明ありがとうございました。
guest

0

参照を取る場合はコンパイルの出力を確認すると「期限切れの変数の読み取りによってエラーが発生しました」となりこれがコンパイルできない直接の理由になります。
このエラーで調べてみるとmsvcのエラーのようでgccではコンパイルできるそうです。

投稿2022/09/20 06:36

vann_2921

総合スコア190

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

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

jbe00214

2022/09/20 10:08

ありがとうございます。ふうむ。期限切れにしないようにする方法はないのでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問