ページ 1 / 1
argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 00:54
by foobar2015
コマンドライン引数に入力ファイル名が与えられたかによって、入力ストリームをcinとifstreamで切り替えたいと思っています。
そこで、次のように書くとcinのコピーが起こってしまうため(?)コンパイルエラーとなり、
コード:
// Error
int main(int argc, char * argv[]){
ifstream & is = argc==2 ? ifstream(argv[1], ios::in) : cin;
}
しかし次のように書くとコンパイルが通ります。
コード:
// OK
int doit(istream & in){
int x; in >> x;
cout << x << endl;
return x;
}
int main(int argc, char * argv[]){
if(argc==2){
ifstream ifs(argv[1], ios::in);
doit(ifs);
} else {
doit(cin);
}
}
これは何故ですか?
また、できれば始めに挙げた方法で書きたいのですが、うまく行く方法はありますか?(良くない書き方だったりするのでしょうか)
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 05:00
by h2so5
std::cinはifstreamを継承していないため、ifstream&をstd::cinで初期化することはできません。
また、ifstream(argv[1], ios::in)は右辺値なので左辺値参照を初期化することもできません。
参照を利用したいのであれば以下のようになると思います。
コード:
int main(int argc, char * argv[]){
ifstream ifs;
istream* isp = &cin;
if (argc == 2) {
ifs.open(argv[1], ios::in);
if (ifs) isp = &ifs;
}
istream &is = *isp;
}
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 07:57
by nullptr
難しい内容ですがh2so5さんに少し補足をします。
non-const lvalue referenceが束縛できるのはlvalueのみです。故に初期化式はlvalueでなければなりません。
ただし、const lvalue referenceの場合は、初期化式の束縛したオブジェクトの寿命が延長されます。const lvalue referenceはlvalueもprvalueもxvalueも束縛できます。
コード:
std::ifstream& s = std::move(std::ifstream("", std::ios::in)); // error
const std::ifstream& s = std::move(std::ifstream("", std::ios::in)); // ok
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 08:22
by nullptr
doitの引数は
istreamの参照になっているのでistreamであるcinが正しく渡せてコンパイルできます。
参照の初期化式の三項演算子の後者二項のvalue categoryを一致させる必要があるようです。規格の該当部分がちょっとわからないので正しい引用ができないのですが、直感的で妥当な仕様であると思います。
故にisは
ifstreamではなく
istreamの参照にし、ifstreamをlvalueにすることでコンパイルできるはずです。
コード:
ifstream f(argv[1], ios::in);
istream& is = ( argc == 2 ) ? f : cin;
ifstreamをrvalueにするなら、cinの方もrvalueにしなくてはならず、以下のようになります。
コード:
const istream& is = ( argc == 2 ) ? ifstream(argv[1], ios::in) : std::move(cin);
istream&& is = ( argc == 2 ) ? ifstream(argv[1], ios::in) : std::move(cin);
const istream&& is = ( argc == 2 ) ? ifstream(argv[1], ios::in) : std::move(cin);
ただし、std::cinをムーブする時点で殆どの場合は不正な動作である可能性が高いので、おすすめしません。
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 13:17
by h2so5
nullptrさん
そのコードだとargcをチェックする前にargv[1]にアクセスしてしまいます。
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 13:33
by zeek
コード:
ifstream ifs;
istream &is = argc == 2 ? ifs = ifstream(argv[1]) : cin;
とか
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 16:11
by nullptr
h2so5 さんが書きました:nullptrさん
そのコードだとargcをチェックする前にargv[1]にアクセスしてしまいます。
OH
Re: argcで出力ストリームを切り替え
Posted: 2015年1月11日(日) 17:46
by foobar2015
ifstreamとcinの変換ができないことを見落としていました。無理に参照にする必要も無さそうですね…
皆様有難うございます。
Re: argcで出力ストリームを切り替え
Posted: 2015年1月12日(月) 09:56
by かずま
foobar2015 さんが書きました:ifstreamとcinの変換ができないことを見落としていました。無理に参照にする必要も無さそうですね…
無理なく参照にできますよ。
コード:
ifstream ifs;
istream & is = (argc == 2) ? (ifs.open(argv[1]), ifs) : cin;
(ifs.open(argv[1], ifs) の代わりに、(ifs = ifstream(argv[1])) だと、
ifstream の一時オブジェクトを生成し、それを代入演算子でコピーし、
一時オブジェクトのデストラクタが呼ばれるという無駄がありますが、
open() なら素直です。