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

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

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

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

Q&A

解決済

2回答

483閲覧

<cstdint> int(NN)_t型のオーバーロードにおける重複をなくしたい

m4697859930

総合スコア16

C++

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

0グッド

0クリップ

投稿2018/12/29 14:40

forward

私は趣味として競技プログラミングをしている高校生です。最近、データの出入力において、cin/cout よりも scanf/printf の方が速いということを知りました。しかし、変換指定子は各クラスによって異なるためコードを打ち込む際の時間短縮やタイプミス対策のため、Visual Studio を用いて新たに入出力用の関数(?)を作ることにしました。

code

C++

1 2#include <iostream> 3#include <cstdio> 4#include <cstdint> 5#include <cinttypes> 6 7struct in 8{ 9 template<typename T> in& operator>>(T& val) { std::cin >> val; return *this; } 10 in& operator>>(char& val) { std::scanf(" %c", &val); return *this; } 11 in& operator>>(std::int_fast8_t& val) { std::scanf("%" SCNdFAST8, &val); return *this; } 12 in& operator>>(std::uint_fast8_t& val) { std::scanf("%" SCNuFAST8, &val); return *this; } 13 in& operator>>(std::int_fast16_t& val) { std::scanf("%" SCNdFAST16, &val); return *this; } 14 in& operator>>(std::uint_fast16_t& val) { std::scanf("%" SCNuFAST16, &val); return *this; } 15 in& operator>>(std::int_fast32_t& val) { std::scanf("%" SCNdFAST32, &val); return *this; } 16 in& operator>>(std::uint_fast32_t& val) { std::scanf("%" SCNuFAST32, &val); return *this; } 17 in& operator>>(std::int_fast64_t& val) { std::scanf("%" SCNdFAST64, &val); return *this; } 18 in& operator>>(std::uint_fast64_t& val) { std::scanf("%" SCNuFAST64, &val); return *this; } 19 in& operator>>(float& val) { std::scanf("%f", &val); return *this; } 20 in& operator>>(double& val) { std::scanf("%lf", &val); return *this; } 21}; 22 23struct out 24{ 25 template<typename T> out& operator<<(T& val) { std::cout << val; return *this; } 26 out& operator<<(char& val) { std::printf("%c", val); return *this; } 27 out& operator<<(std::int_fast8_t& val) { std::printf("%" PRIdFAST8, val); return *this; } 28 out& operator<<(std::uint_fast8_t& val) { std::printf("%" PRIuFAST8, val); return *this; } 29 out& operator<<(std::int_fast16_t& val) { std::printf("%" PRIdFAST16, val); return *this; } 30 out& operator<<(std::uint_fast16_t& val) { std::printf("%" PRIuFAST16, val); return *this; } 31 out& operator<<(std::int_fast32_t& val) { std::printf("%" PRIdFAST32, val); return *this; } 32 out& operator<<(std::uint_fast32_t& val) { std::printf("%" PRIuFAST32, val); return *this; } 33 out& operator<<(std::int_fast64_t& val) { std::printf("%" PRIdFAST64, val); return *this; } 34 out& operator<<(std::uint_fast64_t& val) { std::printf("%" PRIuFAST64, val); return *this; } 35 out& operator<<(float& val) { std::printf("%f", val); return *this; } 36 out& operator<<(double& val) { std::printf("%lf", val); return *this; } 37}; 38 39// ex) int_fast16_t i; に対して in() >> i; out() << i; -> 疑似 cin/cout 40

error

Wandbox (C++ gcc HEAD 9.0.0 20181227 (experimental)) で返ってきたエラーです。

prog.cc:14:6: error: 'in& in::operator>>(int_fast32_t&)' cannot be overloaded with 'in& in::operator>>(int_fast16_t&)' 14 | in& operator>>(std::int_fast32_t& val) { std::scanf("%" SCNdFAST32, &val); return *this; } | ^~~~~~~~ prog.cc:12:6: note: previous declaration 'in& in::operator>>(int_fast16_t&)' 12 | in& operator>>(std::int_fast16_t& val) { std::scanf("%" SCNdFAST16, &val); return *this; } | ^~~~~~~~ prog.cc:15:6: error: 'in& in::operator>>(uint_fast32_t&)' cannot be overloaded with 'in& in::operator>>(uint_fast16_t&)' 15 | in& operator>>(std::uint_fast32_t& val) { std::scanf("%" SCNuFAST32, &val); return *this; } | ^~~~~~~~ prog.cc:13:6: note: previous declaration 'in& in::operator>>(uint_fast16_t&)' 13 | in& operator>>(std::uint_fast16_t& val) { std::scanf("%" SCNuFAST16, &val); return *this; } | ^~~~~~~~ prog.cc:16:6: error: 'in& in::operator>>(int_fast64_t&)' cannot be overloaded with 'in& in::operator>>(int_fast16_t&)' 16 | in& operator>>(std::int_fast64_t& val) { std::scanf("%" SCNdFAST64, &val); return *this; } | ^~~~~~~~ prog.cc:12:6: note: previous declaration 'in& in::operator>>(int_fast16_t&)' 12 | in& operator>>(std::int_fast16_t& val) { std::scanf("%" SCNdFAST16, &val); return *this; } | ^~~~~~~~ prog.cc:17:6: error: 'in& in::operator>>(uint_fast64_t&)' cannot be overloaded with 'in& in::operator>>(uint_fast16_t&)' 17 | in& operator>>(std::uint_fast64_t& val) { std::scanf("%" SCNuFAST64, &val); return *this; } | ^~~~~~~~ prog.cc:13:6: note: previous declaration 'in& in::operator>>(uint_fast16_t&)' 13 | in& operator>>(std::uint_fast16_t& val) { std::scanf("%" SCNuFAST16, &val); return *this; } | ^~~~~~~~ prog.cc:30:7: error: 'out& out::operator<<(int_fast32_t&)' cannot be overloaded with 'out& out::operator<<(int_fast16_t&)' 30 | out& operator<<(std::int_fast32_t& val) { std::printf("%" PRIdFAST32, val); return *this; } | ^~~~~~~~ prog.cc:28:7: note: previous declaration 'out& out::operator<<(int_fast16_t&)' 28 | out& operator<<(std::int_fast16_t& val) { std::printf("%" PRIdFAST16, val); return *this; } | ^~~~~~~~ prog.cc:31:7: error: 'out& out::operator<<(uint_fast32_t&)' cannot be overloaded with 'out& out::operator<<(uint_fast16_t&)' 31 | out& operator<<(std::uint_fast32_t& val) { std::printf("%" PRIuFAST32, val); return *this; } | ^~~~~~~~ prog.cc:29:7: note: previous declaration 'out& out::operator<<(uint_fast16_t&)' 29 | out& operator<<(std::uint_fast16_t& val) { std::printf("%" PRIuFAST16, val); return *this; } | ^~~~~~~~ prog.cc:32:7: error: 'out& out::operator<<(int_fast64_t&)' cannot be overloaded with 'out& out::operator<<(int_fast16_t&)' 32 | out& operator<<(std::int_fast64_t& val) { std::printf("%" PRIdFAST64, val); return *this; } | ^~~~~~~~ prog.cc:28:7: note: previous declaration 'out& out::operator<<(int_fast16_t&)' 28 | out& operator<<(std::int_fast16_t& val) { std::printf("%" PRIdFAST16, val); return *this; } | ^~~~~~~~ prog.cc:33:7: error: 'out& out::operator<<(uint_fast64_t&)' cannot be overloaded with 'out& out::operator<<(uint_fast16_t&)' 33 | out& operator<<(std::uint_fast64_t& val) { std::printf("%" PRIuFAST64, val); return *this; } | ^~~~~~~~ prog.cc:29:7: note: previous declaration 'out& out::operator<<(uint_fast16_t&)' 29 | out& operator<<(std::uint_fast16_t& val) { std::printf("%" PRIuFAST16, val); return *this; } | ^~~~~~~~

problem

非常に長いエラーですが、今回問題となっているのは**「int(NN)_t型は <cstdint> で typedef されているだけなので、クラスの衝突が発生してしまう」**という点です。基本形を使えば済む話ではあるのですが、どうにかして int(NN)_t型を用いたコードを作りたいのです。多重定義を回避する方法、もしくはこの関数(?)を成立させる方法をご教示いただけると幸いです。

supplementation

コードを修正するためgoogle 先生に頼り、「サイズ別に整数型を多重定義したかった - イグトランスの頭の中」->「整数型の大きさで関数をオーバーロードする方法 - Qiita」のサイトを見させていただきました。しかし、プログラミング経験の浅い私には内容が把握しきれませんでした。もし私がより C++ に精通していれば、何かしらのヒントになっていたと思います。

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

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

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

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

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

guest

回答2

0

ベストアンサー

こんにちは。

SFINAEは本当に難しいですもんね。
C++17で規定された constexpr ifを使う方法なら比較的分かりやすいかも。

C++

1#include <type_traits> 2#include <iostream> 3#include <cstdio> 4#include <cstdint> 5#include <cinttypes> 6 7struct out 8{ 9 template<typename T> 10 out& operator<<(T const& val) 11 { 12 if constexpr (std::is_same_v<T, int_fast8_t>) std::printf("%" PRIdFAST8, val); 13 else if constexpr (std::is_same_v<T, uint_fast8_t>) std::printf("%" PRIuFAST8, val); 14 else if constexpr (std::is_same_v<T, int_fast16_t>) std::printf("%" PRIdFAST16, val); 15 else if constexpr (std::is_same_v<T, uint_fast16_t>)std::printf("%" PRIuFAST16, val); 16 else if constexpr (std::is_same_v<T, int_fast32_t>) std::printf("%" PRIdFAST32, val); 17 else if constexpr (std::is_same_v<T, uint_fast32_t>)std::printf("%" PRIuFAST32, val); 18 else if constexpr (std::is_same_v<T, int_fast64_t>) std::printf("%" PRIdFAST64, val); 19 else if constexpr (std::is_same_v<T, uint_fast64_t>)std::printf("%" PRIuFAST64, val); 20 else if constexpr (std::is_same_v<T, float>) std::printf("%f", val); 21 else if constexpr (std::is_same_v<T, double>) std::printf("%lf", val); 22 else std::cout << val; 23 24 return *this; 25 } 26}; 27 28int main() 29{ 30 out xout; 31 xout << static_cast<uint32_t >(123) << "\n"; 32 xout << static_cast<unsigned int >(456) << "\n"; 33 xout << static_cast<unsigned long>(789)<< "\n"; 34}

constexpr ifの場合、constexpr if文の条件が成立していない節についてはテンプレート引数が決まらないと確定しない部分は評価されないのでコンパイル・エラーを回避できます。
上記のconstexprを外してみるとエラーがでるので雰囲気を掴めるかもです。
wandbox

基本形を使えば済む話ではあるのですが、

全くその通りですので

どうにかして int(NN)_t型を用いたコードを作りたいのです。

は何故なのでしょう? ちょっと興味深いです。

投稿2018/12/29 15:34

編集2018/12/29 15:37
Chironian

総合スコア23272

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

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

m4697859930

2018/12/29 16:34 編集

回答ありがとうございます。 ・どうにかしてint(NN)_t型を用いたかった理由について 使いたいと思ったきっかけはあるサイト(https://postd.cc/how-to-c-in-2016-1/)を見たからです。 <cstdint> を覗いた限りでは int_fast(NN)型は typedef での定義でしたので、イマイチぴんと来なかったのですが、そのサイトによると 「64ビットのシステムで uint_fast16_t を要求すると、ワードサイズの整数で操作する方が、16ビットの整数で操作するよりも速いため uint64_t を得る。必要とするのは16ビットの整数だがプラットフォームでの処理を早くするために64ビットの整数が使いたいという時に、uint_fast16_tを使うことで、int型と同様にプラットフォームによってサイズが変わる不確かさがあってもコード内の安全だと分かっている範囲に不確かさを制限できる。」 とあったのです。(これはサイトの一部を要約しただけですので、一度サイトを見ていただいた方が早いかと思います。) 競技プログラミングでは速さとメモリ使用量が鍵となるのですが自分の課題が速さの面にあったため、今回多少複雑になっても int_fast(NN)_t型での実装を知りたいと思い、質問させていただきました。 もしご期待に沿えない回答、もしくは的外れな回答となっていたら申し訳ないです。 ・今回回答いただいたコードについて 今回書いていただいたconstexpr if を使ったコードは理解できました。ありがとうございます。 お手数をお掛けして恐縮ですが、一つ質問をさせてください。競技プログラミング(自分の出る大会)はまだ C++17 が使えず C++14(最悪 C++11)までの場合が多いのです。もし C++17 を使わずに C++14 までの実装となると現実的に難しいのでしょうか。
Chironian

2018/12/29 17:08

in、outクラス側は基本型を全て定義しておきさえすれば問題ないように感じます。 数が多いのでちょっと手間はかかりますが。http://www.cplusplus.com/reference/type_traits/is_integral/ 呼び出す方は、高速なfast系で定義しても、in, outクラスの適切なオーバーロードが呼ばれますよ。 > もし C++17 を使わずに C++14 までの実装となると現実的に難しいのでしょうか。 例えば、下記のようなtypedefが存在するようなシステムを想定すると、int_fast16_t と int_fast32_tは同じものですから、SFINAEによる分岐は厳しそうです。(同じ定義が2つあるエラーになりそう。) typedef int int_fast16_t; typedef int int_fast32_t; 素直に、基本型全部でオーバーロードするのがベストですよ。その目的ならint_fast*_tでオーバーロードする意味はないですから。
m4697859930

2018/12/30 02:56

回答ありがとうございます。たまたま見た記事が Chironian さんのものだったので回答依頼させていただきましたが、引き受けて下さって感謝してます。本当に助かりました。これから基本形での作成に取り掛かろうと思います。
guest

0

SFINAEを使ったやり方です。

c++

1struct in 2{ 3 template<typename T> 4 auto operator >>(T& val) -> std::enable_if_t<std::is_signed_v<T> && sizeof(T) == sizeof(int8_t), in&> 5 {std::scanf("%" SCNd8, &val); return *this;} 6 7 template<typename T> 8 auto operator >>(T& val) -> std::enable_if_t<std::is_unsigned_v<T> && sizeof(T) == sizeof(uint8_t), in&> 9 {std::scanf("%" SCNu8, &val); return *this;} 10 11 template<typename T> 12 auto operator >>(T& val) -> std::enable_if_t<std::is_signed_v<T> && sizeof(T) == sizeof(int16_t), in&> 13 {std::scanf("%" SCNd16, &val); return *this;} 14 15 template<typename T> 16 auto operator >>(T& val) -> std::enable_if_t<std::is_unsigned_v<T> && sizeof(T) == sizeof(uint16_t), in&> 17 {std::scanf("%" SCNu16, &val); return *this;} 18 19 template<typename T> 20 auto operator >>(T& val) -> std::enable_if_t<std::is_signed_v<T> && sizeof(T) == sizeof(int32_t), in&> 21 {std::scanf("%" SCNd32, &val); return *this;} 22 23 template<typename T> 24 auto operator >>(T& val) -> std::enable_if_t<std::is_unsigned_v<T> && sizeof(T) == sizeof(uint32_t), in&> 25 {std::scanf("%" SCNu32, &val); return *this;} 26 27 template<typename T> 28 auto operator >>(T& val) -> std::enable_if_t<std::is_signed_v<T> && sizeof(T) == sizeof(int64_t), in&> 29 {std::scanf("%" SCNd64, &val); return *this;} 30 31 template<typename T> 32 auto operator >>(T& val) -> std::enable_if_t<std::is_unsigned_v<T> && sizeof(T) == sizeof(uint64_t), in&> 33 {std::scanf("%" SCNu64, &val); return *this;} 34};

投稿2018/12/29 15:41

catsforepaw

総合スコア5938

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

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

m4697859930

2018/12/29 16:38

回答ありがとうございます。 お手数をお掛けして恐縮ですが、一つ質問をさせてください。C++日本語リファレンス(https://cpprefjp.github.io/reference/cstdint.html)によると int(NN)_t型の実装は処理系定義とありました。今回書いていただいた int(NN)_T型を int_fast(NN)_t型に変える場合、scanf の変換指定子も変えるという操作を行うだけでよいのでしょうか。それとも、_fast型はプラットフォームに適した形をとるため(https://postd.cc/how-to-c-in-2016-1/)何らかの工夫がいるのでしょうか。
catsforepaw

2018/12/30 01:18

> int(NN)_t型の実装は処理系定義とありました。 特殊なプロセッサー(例えばスパコンに使われているものとかGPUとか)では、演算処理のbit長が固定なものもあるようです(char/short/int/long/long longすべて同じサイズ!)。そういった処理系ではint##_t型が実装できません。処理系定義とはそういうことです。 一般に出回っているPCやスマホで使われているような汎用のCPUでは、演算処理のbit長が選択可能なのでint##_t系が実装されています。 > 今回書いていただいた int(NN)_T型を int_fast(NN)_t型に変える場合、scanf の変換指定子も変えるという操作を行うだけでよいのでしょうか。 ダメですね。必ずint##_t系を使ってください。というのも、int_fast##_t系は必ずしもその数字通りのビット数とは限りません。実際、VC++ではint_fast16_tとint_fast32_tが同じ定義です。これではsizeof(T)による区別が付きません。 > それとも、_fast型はプラットフォームに適した形をとるため(https://postd.cc/how-to-c-in-2016-1/)何らかの工夫がいるのでしょうか。 私は、(特殊な用途でもない限り)int_fast##_tは使うべきではないと考えます。演算においては、オーバーフローなどの影響も考慮する場面がありますが、bit長は考慮すべき重要な情報だからです。前述の通り、数字通りのbit長とは限りません。つまり、int_fast##_t系を使うと、処理系によって結果が変わる可能性があるということです。
m4697859930

2018/12/30 03:00 編集

腑に落ちなかった部分がようやく理解できました。回答ありがとうございました。 ※追記 どの int(NN)_t型にも当てはまらない場合( string 等)のみ cin を用いることにしたい場合はどんなコードを加えればよいのでしょうか。
catsforepaw

2018/12/30 03:37

普通に引数としてその型を指定するだけで良いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問