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

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

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

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

Q&A

解決済

2回答

1281閲覧

基本演算を備えた2次元配列を作る(再)

Y.R.T

総合スコア42

C++

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

0グッド

0クリップ

投稿2020/08/17 04:27

すみません。
まとめて質問すればよかったのですが。
構文内に、

constexpr T& at(size_t const r, size_t const c)
{
return arr.at(r*C + c);
}

constexpr T const & at(size_t const r, size_t const c) const
{
return arr.at(r*C + c);
}

がありますが。
最初のT& at(size_t const r, size_t const c)
は必要ですか?
どういう理由で記述されているのでしょうか?

#include <iostream> #include <vector> #include <algorithm> #include <iterator> template <class T, size_t R, size_t C> class array2d { typedef T value_type; typedef value_type* iterator; typedef value_type const* const_iterator; std::vector<T> arr; public: array2d() :arr(R*C) {} explicit array2d(std::initializer_list<T> l):arr(l) {} constexpr T* data() noexcept { return arr.data(); } constexpr T const * data() const noexcept { return arr.data(); } constexpr T& at(size_t const r, size_t const c) { return arr.at(r*C + c); } constexpr T const & at(size_t const r, size_t const c) const { return arr.at(r*C + c); } constexpr T& operator() (size_t const r, size_t const c) { return arr[r*C + c]; } constexpr T const & operator() (size_t const r, size_t const c) const { return arr[r*C + c]; } constexpr bool empty() const noexcept { return R == 0 || C == 0; } constexpr size_t size(int const rank) const { if (rank == 1) return R; else if (rank == 2) return C; throw std::out_of_range("Rank is out of range!"); } void fill(T const & value) { std::fill(std::begin(arr), std::end(arr), value); } void swap(array2d & other) noexcept { arr.swap(other.arr); } const_iterator begin() const { return arr.data(); } const_iterator end() const { return arr.data() + arr.size(); } iterator begin() { return arr.data(); } iterator end() { return arr.data() + arr.size(); } }; template <class T, size_t R, size_t C> void print_array2d(array2d<T, R, C> const & arr) { for (int i = 0; i < R; ++i) { for (int j = 0; j < C; ++j) { std::cout << arr.at(i, j) << ' '; } std::cout << std::endl; } } int main() { { std::cout << "test fill" << std::endl; array2d<int, 2, 3> a; a.fill(1); print_array2d(a); } { std::cout << "test operator()" << std::endl; array2d<int, 2, 3> a; for (size_t i = 0; i < a.size(1); ++i) { for (size_t j = 0; j < a.size(2); ++j) { a(i, j) = 1 + i * 3 + j; } } print_array2d(a); } { std::cout << "test move semantics" << std::endl; array2d<int, 2, 3> a{10,20,30,40,50,60}; print_array2d(a); array2d<int, 2, 3> b(std::move(a)); print_array2d(b); } { std::cout << "test swap" << std::endl; array2d<int, 2, 3> a { 1,2,3,4,5,6 }; array2d<int, 2, 3> b { 10,20,30,40,50,60 }; print_array2d(a); print_array2d(b); a.swap(b); print_array2d(a); print_array2d(b); } { std::cout << "test capacity" << std::endl; array2d<int, 2, 3> const a { 1,2,3,4,5,6 }; for (size_t i = 0; i < a.size(1); ++i) { for (size_t j = 0; j < a.size(2); ++j) { std::cout << a(i, j) << ' '; } std::cout << std::endl; } } { std::cout << "test iterators" << std::endl; array2d<int, 2, 3> const a{ 1,2,3,4,5,6 }; for (auto const e : a) { std::cout << e << ' '; } std::cout << std::endl; std::copy( std::begin(a), std::end(a), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } }

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

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

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

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

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

guest

回答2

0

ベストアンサー

しっかりと実装するのであれば必要です。

cpp

1T& at(size_t const r, size_t const c)

cpp

1T const & at(size_t const r, size_t const c) const

の違いはまず、戻り値がT&であるかT const&であるかです。

前者のメンバ関数はT&型、つまり書き換え可能な参照を返しています。
対して後者のメンバ関数はT const&型、つまり書き換え不可能な参照を返しています。

この関数の呼び分けはどうなっているかと言うと、、、

cpp

1T const & at(size_t const r, size_t const c) const 2// ^~~~~ 3// メンバ関数のconst修飾

この後者の関数宣言の末尾のメンバ関数のconst修飾という機能が使われています。
const修飾されていないメンバ関数はconstな変数から呼び出すことはできません。

具体的には、中身を書き換えることができるかどうかの違いがあります。
次のような挙動になります。

cpp

1array2d<int> arr1 = {...}; 2arr1.at(0, 0) = 1; // OK 3// constではない変数なので前者の関数が呼ばれ、ミュータブルな参照が返ってくるので書き換えが可能 4 5const array2d<int> arr2 = {...}; 6arr2.at(0, 0) = 1; // ERROR 7// constな変数なので後者の関数が呼ばれ、const参照返ってくる 8// constな変数に代入しようとしてコンパイルエラー

投稿2020/08/17 04:52

mitama_rs

総合スコア165

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

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

Y.R.T

2020/08/17 05:02

ありがとうございます。 理解できました。
guest

0

最初のT& at(size_t const r, size_t const c)

は必要ですか?

obj.at(2, 3) = 5;のように、左辺にして代入ができるようになります。

投稿2020/08/17 04:37

maisumakun

総合スコア146018

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

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

Y.R.T

2020/08/17 05:03

ありがとうございます。 理解できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問