effective modern c++ 勉強会#7 item 27
TRANSCRIPT
Effective Modern C++勉強会#7Item 27
刈谷 満(@kariya_mitsuru)
2015/6/17
Item 27: Familiarize yourself with alternativesto overloading on universal references.
ユニバーサルリファレンスを伴う
オーバーロードの代替案に
慣れ親しもう
Item 27: Familiarize yourself with alternativesto overloading on universal references.
Item 26 で見たように、ユニバーサルリファレンスを伴うオーバーロードはヤバい
でも、オーバーロード便利(ただし、思った通りに動いてくれれば)
Item 27: Familiarize yourself with alternativesto overloading on universal references.
こんな代替案がある
1. オーバーロードを諦める2. const T& で渡す3. 値で渡す4. タグディスパッチを使う5. ユニバーサルリファレンスをとるテンプレートに制約を設ける
Item 27: Familiarize yourself with alternativesto overloading on universal references.
1.オーバーロードを諦める
そのまんま
オーバーロードするから訳が分からなくなる
⇓
オーバーロードしなきゃいんじゃね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
こうじゃ
template<typename T>
void logAndAddName(T&& name);
void logAndAddNameIdx(int idx);
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
こうじゃ
template<typename T>
void logAndAddName(T&& name);
void logAndAddNameIdx(int idx);
全て解決!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
欠点
コンストラクタには無力(コンストラクタには名前が無い)
本には名前固定って書いてあるけど、名前無いっすよ…
§12[class.ctor]/p.1 "Constructors do not have names."
てか、やっぱオーバーロード使いたくね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
2.const T& で渡す
そのまんま
ユニバーサルリファレンス使うから訳がわからなくなる
⇓
ユニバーサルリファレンス使わなきゃいんじゃね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
こうじゃ
void logAndAdd(const std::string&
name);
void logAndAdd(int idx);
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
void logAndAdd(T&& name);
void logAndAdd(int idx);
こうじゃ
void logAndAdd(const std::string&
name);
void logAndAdd(int idx);
全て解決!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
欠点
ユニバーサルリファレンス使う方法ほど効率的じゃない
(でもシンプルさは魅力的)
てか、やっぱユニバーサルリファレンス使いたくね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
3.値で渡す
そのまんま
…え?効率は?
実はわりと効率がいい(場合も多い)
けど、詳細は Item 41 のお楽しみ
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
explicit Person(T&& n)
: name(std::forward<T>(n)) {}
explicit Person(int idx)
: name(nameFromIdx(idx)) {}
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
explicit Person(T&& n)
: name(std::forward<T>(n)) {}
explicit Person(int idx)
: name(nameFromIdx(idx)) {}
こうじゃ
explicit Person(std::string n)
: name(std::move(n)) {}
explicit Perosn(int idx)
: name(nameFromIdx(idx)) {}
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これを
template<typename T>
explicit Person(T&& n)
: name(std::forward<T>(n)) {}
explicit Person(int idx)
: name(nameFromIdx(idx)) {}
こうじゃ
explicit Person(std::string n)
: name(std::move(n)) {}
explicit Perosn(int idx)
: name(nameFromIdx(idx)) {}
全て解決!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
欠点この例ならわりと効率いいけど、
ユニバーサルリファレンスほどじゃないし、ムーブが効率悪い場合には使えない。(std::array とか…)
Item 41 を知らない人が見ると、「効率悪い!」と言われて、最悪直される(※ 個人の感想です)
0 はともかく、NULL を渡すと int バージョン呼ばれる。(けど、これに限らないし、Item 8 読め)
Item 27: Familiarize yourself with alternativesto overloading on universal references.
4.タグディスパッチを使う
やっぱ完全転送したい!!!
⇓
みんな大好き、タグディスパッチ
Item 27: Familiarize yourself with alternativesto overloading on universal references.
わあい
タグディスパッチめう
めうタグディスパッチ
大好きめう
©芽兎めう
Item 27: Familiarize yourself with alternativesto overloading on universal references.
ユニバーサルリファレンスの問題点
あらゆる引数に完全一致する
Item 27: Familiarize yourself with alternativesto overloading on universal references.
引数が
一つしかないと
錯覚していた?
©藍染惣右介
Item 27: Familiarize yourself with alternativesto overloading on universal references.
解決策
ユニバーサルリファレンス以外の引数で、オーバーロード解決
を制御すれば良い
Item 27: Familiarize yourself with alternativesto overloading on universal references.
元のコード
std::multiset<std::string> names;
template<typename T>
void logAndAdd(T&& name)
{
auto now = std::chrono::system_clock::now();
log(now, "logAndAdd");
names.emplace(std::forward<T>(name));
}
これに、int を取るオーバーロードを追加したい
Item 27: Familiarize yourself with alternativesto overloading on universal references.
logAndAdd のオーバーロードを追加するんじゃなくて、logAndAdd 内で実際に処理する関数logAndAddImpl に処理を委譲する
⇓この際、T が int の場合用と
そうじゃない場合用の関数を作って
それらの関数に追加の引数を追加して、
それらの型(タグ)が完璧に異なれば良い
Item 27: Familiarize yourself with alternativesto overloading on universal references.
クライアントが呼び出す関数
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(name,
型①か型②);
}
実際に処理する関数
void logAndAddImpl(T&& name,
型①)
{
int 以外の時の処理がここに}
void logAndAddImpl(int idx,
型②)
{
int の時の処理がここに}Tが intの以外時は「型①」
Tが intの時は「型②」
つまり、こんな感じにすれば良い
Item 27: Familiarize yourself with alternativesto overloading on universal references.
クライアントが呼び出す関数
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(name,
型①か型②);
}
実際に処理する関数
void logAndAddImpl(T&& name,
型①)
{
int 以外の時の処理がここに}
void logAndAddImpl(int idx,
型②)
{
int の時の処理がここに}Tが intの以外時は「型①」
Tが intの時は「型②」
つまり、こんな感じにすれば良い
で?「型①か型②」って?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これが「型①か型②」だ!!!
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(std::forward<T>(name),
std::is_integral<T>());
}引数だから、()を付けてオブジェクト生成
Tが int以外の時は「std::false_type」から派生Tが intの時は「std::true_type」から派生
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これが「型①か型②」だ!!!
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(std::forward<T>(name),
std::is_integral<T>());
}引数だから、()を付けてオブジェクト生成
Tが int以外の時は「std::false_type」から派生Tが intの時は「std::true_type」から派生
でもちょっとうまくない…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
int i = 10;
…
logAndAdd(i);
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(
std::forward<T>(name),
std::is_integral<T>());
}
うまくない理由
こんな感じで呼び出すと…T が int& になっちゃう
⇒参照型は整数型じゃないから、std::false_typeになっちゃう
Item 27: Familiarize yourself with alternativesto overloading on universal references.
これが正しい「型①か型②」だ!!!
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(
std::forward<T>(name),
std::is_integral<typename std::remove_reference<T>::type>());
}
Tが intっぽくない時は「std::false_type」Tが intっぽい時は「std::true_type」
Tから「参照」を消す
Item 27: Familiarize yourself with alternativesto overloading on universal references.
ちな、処理本体側はこんな感じ
// int 以外用の関数template<typename T>void logAndAddImpl(T&& name, std::false_type){auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}
std::string nameFromIdx(int idx);
// int 用の関数void logAndAddImpl(int idx, std::true_type){
logAndAdd(nameFromIdx(idx));}
オーバーロード解決のためだけにあるから、パラメータ名は付けてない
オーバーロード解決のためだけにあるから、パラメータ名は付けてない
idx から名前に変換したら、
再度 logAndAdd を呼び出す
Item 27: Familiarize yourself with alternativesto overloading on universal references.
ちな、処理本体側はこんな感じ
// int 以外用の関数template<typename T>void logAndAddImpl(T&& name, std::false_type){auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}
std::string nameFromIdx(int idx);
// int 用の関数void logAndAddImpl(int idx, std::true_type){
logAndAdd(nameFromIdx(idx));}
オーバーロード解決のためだけにあるから、パラメータ名は付けてない
オーバーロード解決のためだけにあるから、パラメータ名は付けてない
idx から名前に変換したら、
再度 logAndAdd を呼び出す
賢いコンパイラなら、最後のパラメータ使ってないから
消してくれるかも…
こうすれば、ログ出力コード二重にメンテしなくて済む…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
タグディスパッチSUGEEEE!!!!
タグディスパッチのこと
もっと知りたい!!!
でも EMC++ にはそこまで書かれてない…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
そんなあなたに C++テンプレートテクニック 第2版
あのえぴさんとアキラさんが
書いてるぞ!!!
C++11完全対応!!!
電子書籍版もあるぞ!!!ちょっと読みづらいけど…
タグディスパッチだけじゃなくてテンプレート全般!!!
まだ全部理解できてないけど…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
タグディスパッチいいよ、タグディスパッチ
もう何もかもタグディスパッチでよくね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
欠点
コンストラクタには無力
(またっすか…)
Item 27: Familiarize yourself with alternativesto overloading on universal references.
例のダメなクラスclass Person {
public:
template<typename T>
explicit Person(T&& n)
: name(std::forward<Person&>(n)) {}
explicit Person(int idx);
Person(const Person& rhs);
Person(Person&& rhs);
…
};
完全転送コンストラクタ
intコンストラクタ コピー
コンストラクタ
ムーブコンストラクタ
Item 27: Familiarize yourself with alternativesto overloading on universal references.
例のダメなクラスclass Person {
public:
template<typename T>
explicit Person(T&& n)
: name(std::forward<Person&>(n)) {}
explicit Person(int idx);
Person(const Person& rhs);
Person(Person&& rhs);
…
};
Person p("Nancy");
auto cloneOfP(p);
全部完全転送…
上はいいけど、下に対してコピーコンストラクタが
呼ばれない…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
5.ユニバーサルリファレンスをとるテンプレートに制約を設ける
そのまんま
ユニバーサルリファレンスを取る関数が呼ばれて欲しくない時まで呼ばれちゃう
⇓
関数を呼ばれて欲しい時だけ有効にすればいんじゃね?
Item 27: Familiarize yourself with alternativesto overloading on universal references.
「関数を呼ばれて欲しい時だけ有効に」?
そのためのメタ関数が type_traits に!
std::enable_if
SFINAE と呼ばれる機構を使ってる
Item 27: Familiarize yourself with alternativesto overloading on universal references.
使い方はこんな感じ
class Person {public:
template<typename T,typename = typename std::enable_if<condition>::type>
explicit Person(T&& n);…
}; この condition の部分に、このコンストラクタを使っても良い条件式を書く。
ただし、コンパイル時定数のみ!!
関数テンプレートのデフォルトテンプレート引数(C++11の新機能!)を enable_if にする
Item 27: Familiarize yourself with alternativesto overloading on universal references.
では、今回の場合、どんな時に呼ばれて欲しいのか?⇓
T が Person じゃない時T が Person の場合、コピーコンストラクタやムーブコンストラクタが呼ばれて欲しいから
そんな時も type_traits
Item 27: Familiarize yourself with alternativesto overloading on universal references.
こんな式
!std::is_same<Person, T>::value
先頭の!に注意 Person と T が同じ型の時 true
型じゃなくてbool 値が欲しいから
::value
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {public:template<
typename T,typename = typename std::enable_if<!std::is_same<Person, T>::value
>::type>explicit Person(T&& n);
Person(const Person&);
Person(Person&&);…
};
ダメでした…
Person p("Nancy");
auto cloneOfP(p);
T はconst char(&)[6]
T はPerson&
p は左辺値だからT が左辺値参照に…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
T と Person を比較する時には、以下の2点を無視したい
•参照かどうか引数の型が Person だろうが Person& だろうが Person&& だろうが、完全転送コンストラクタじゃ無くてコピー・ムーブコンストラクタが呼ばれてほしい
• const や volatile が付いているかどうか引数の型が const Person だろうが volatile Person だろうが const volatile Person だろうが、完全転送コンストラクタじゃ無k(以下略
そんな時も type_traits
Item 27: Familiarize yourself with alternativesto overloading on universal references.
こんな式
!std::is_same<Person,
typename std::decay<T>::type
>::value
ホントはこれだと、配列や関数をポインタに変換しちゃうけど、今回の場合は問題なし
参照を取ってから、更に cv 修飾を取る!!
値じゃなくて型が欲しいから ::type
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {public:template<
typename T,typename = typename std::enable_if<
!std::is_same<Person,typename std::decay<T>::type
>::value>::type
>explicit Person(T&& n);
Person(const Person&);
Person(Person&&);…
};
まだダメでした…class SP : public Person {public:SP(const SP& rhs): Person(rhs){ … }
SP(SP&& rhs): Person(std::move(rhs)){ … }
…};
T はconst SP&
T はSP&&
Item 27: Familiarize yourself with alternativesto overloading on universal references.
完全転送コンストラクタは、
T が Person かその派生クラスの時には
無効化されて欲しい
そんな時も type_traits
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {
public:
template<
typename T,
typename = typename std::enable_if<
!std::is_base_of<Person,
typename std::decay<T>::type
>::value
>::type
>
explicit Person(T&& n);
…
};
typename std::decay<T>::type がPerson の派生クラスの時 true
Person そのものでもOK!
だいぶ長い…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {
public:
template<
typename T,
typename = typename std::enable_if<
!std::is_base_of<Person,
typename std::decay<T>::type
>::value
>::type
>
explicit Person(T&& n);
…
};
typename std::decay<T>::type がPerson の派生クラスの時 true
Person そのものでもOK!
だいぶ長い…
Errata に補足あり!std::is_base_of<int, int> は
false になるよ!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {
public:
template<
typename T,
typename = std::enable_if_t<
!std::is_base_of<Person,
std::decay_t<T>
>::value
>
>
explicit Person(T&& n);
…
};
ちなみに C++14 だったらもうちょっとスマート(かな?)
typename と ::type が必要ない!
C++14 でも ::value は必要(大人の事情により _v は入らず…)
Item 27: Familiarize yourself with alternativesto overloading on universal references.
でもまだ int を引数にとる
コンストラクタが追加されてない…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {public:template<typename T,
typename = std::enable_if_t<!std::is_base_of<Person, std::decay_t<T>>::value&&!std::is_integral<std::remove_reference_t<T>>::value
>>explicit Person(T&& n): name(std::forward<T>(n)){ … }
explicit Person(int idx): name(nameFromIdx(idx)){ … }
…
private:std::string name;
};
C++14 でも超絶長い…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
SFINAE SUGEEEE!!!!
SFINAEのこと
もっと知りたい!!!
でも EMC++ にはそこまで書かれてない…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
そんなあなたに C++テンプレートテクニック 第2版
あのえぴさんとアキラさんが
書いてるぞ!!!
C++11完全対応!!!
電子書籍版もあるぞ!!!ちょっと読みづらいけど…
SFINAEだけじゃなくてテンプレート全般!!!
まだ全部理解できてないけど…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
トレードオフ
1. オーバーロードを諦める
2. const T& で渡す
3. 値で渡す
4. タグディスパッチを使う
5. ユニバーサルリファレンスをとるテンプレートに制約を設ける
完全転送を諦める(シンプル)
完全転送を使う(効率いい)
Item 27: Familiarize yourself with alternativesto overloading on universal references.
完全転送の欠点
1.完全転送できない場合がある(完全じゃね~し… Item 30を参照)
2.クライアントが誤った引数を渡した時、とち狂ったエラーメッセージが出る
Item 27: Familiarize yourself with alternativesto overloading on universal references.
とち狂ったメッセージの例
例えば、こんな感じに間違った場合…
Person p(u"Konrad Zuse");
巷で話題のutf-16 リテラル
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc: In instantiation of 'Person::Person(T&&) [with T = const char32_t (&)[5]; <template-parameter-1-2> = void]':prog.cc:30:18: required from hereprog.cc:16:27: error: no matching function for call to 'std::__cxx11::basic_string<char>::basic_string(const char32_t [5])': name(std::forward<T>(n))
^In file included from /usr/local/gcc-head/include/c++/6.0.0/string:52:0,
from prog.cc:2:/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:535:9: note: candidate: template<class _InputIterator, class> std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(_InputIterator, _InputIterator, const _Alloc&)
basic_string(_InputIterator __beg, _InputIterator __end,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:535:9: note: template argument deduction/substitution failed:prog.cc:16:27: note: candidate expects 3 arguments, 1 provided: name(std::forward<T>(n))
^In file included from /usr/local/gcc-head/include/c++/6.0.0/string:52:0,
from prog.cc:2:/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:512:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(basic_string&& __str, const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:512:7: note: candidate expects 2 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:508:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const basic_string& __str, const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:508:7: note: candidate expects 2 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:504:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::initializer_list<_Tp>, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:504:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'std::initializer_list<char>'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:477:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(basic_string&& __str) noexcept^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:477:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'std::__cxx11::basic_string<char>&&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:465:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, _CharT, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(size_type __n, _CharT __c, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:465:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:455:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:455:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const char*'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:445:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const _CharT* __s, size_type __n,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:445:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:427:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const basic_string& __str, size_type __pos,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:427:7: note: candidate expects 4 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:411:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const basic_string& __str, size_type __pos,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:411:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:399:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const basic_string& __str)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:399:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const std::__cxx11::basic_string<char>&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:391:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:391:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const std::allocator<char>&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:380:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string()^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:380:7: note: candidate expects 0 arguments, 1 provided
GCCさん
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc:16:4: error: no matching constructor for initialization of 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >'): name(std::forward<T>(n))^ ~~~~~~~~~~~~~~~~~~
prog.cc:30:9: note: in instantiation of function template specialization 'Person::Person<char32_t const (&)[5], void>' requested herePerson p(U"name");
^/usr/local/libcxx-head/include/c++/v1/string:1326:40: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const allocator_type' (aka 'const std::__1::allocator<char>') for 1st argument
_LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)^
/usr/local/libcxx-head/include/c++/v1/string:1333:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const std::__1::basic_string<char>' for 1st argumentbasic_string(const basic_string& __str);^
/usr/local/libcxx-head/include/c++/v1/string:1338:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'std::__1::basic_string<char>' for 1st argumentbasic_string(basic_string&& __str)^
/usr/local/libcxx-head/include/c++/v1/string:1348:31: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const value_type *' (aka 'const char *') for 1st argument_LIBCPP_INLINE_VISIBILITY basic_string(const value_type* __s);
^/usr/local/libcxx-head/include/c++/v1/string:1369:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'initializer_list<value_type>' for 1st argument
basic_string(initializer_list<value_type> __il);^
/usr/local/libcxx-head/include/c++/v1/string:1363:9: note: candidate constructor template not viable: requires 2 arguments, but 1 was providedbasic_string(_InputIterator __first, _InputIterator __last);^
/usr/local/libcxx-head/include/c++/v1/string:1366:9: note: candidate constructor template not viable: requires 3 arguments, but 1 was providedbasic_string(_InputIterator __first, _InputIterator __last, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1323:31: note: candidate constructor not viable: requires 0 arguments, but 1 was provided_LIBCPP_INLINE_VISIBILITY basic_string()
^/usr/local/libcxx-head/include/c++/v1/string:1346:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(basic_string&& __str, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1334:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const basic_string& __str, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1350:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const value_type* __s, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1352:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const value_type* __s, size_type __n);^
/usr/local/libcxx-head/include/c++/v1/string:1356:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(size_type __n, value_type __c);^
/usr/local/libcxx-head/include/c++/v1/string:1371:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(initializer_list<value_type> __il, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1354:5: note: candidate constructor not viable: requires 3 arguments, but 1 was providedbasic_string(const value_type* __s, size_type __n, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1358:5: note: candidate constructor not viable: requires 3 arguments, but 1 was providedbasic_string(size_type __n, value_type __c, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1359:5: note: candidate constructor not viable: requires at least 2 arguments, but 1 was providedbasic_string(const basic_string& __str, size_type __pos, size_type __n = npos,^
Clangさん
Item 27: Familiarize yourself with alternativesto overloading on universal references.
この例の場合、ユニバーサルリファレンスの引数は
std::string の初期化に使われることが分かっているので、
static_assert でチェックすることはできる。
Item 27: Familiarize yourself with alternativesto overloading on universal references.
class Person {public:
template<typename T,typename = std::enable_if_t<
!std::is_base_of<Person, std::decay_t<T>>::value&&!std::is_integral<std::remove_reference_t<T>>::value
>>explicit Person(T&& n): name(std::forward<T>(n)){
// assert that a std::string can be created from a T objectstatic_assert(
std::is_constructible<std::string, T>::value,"Parameter n can't be used to construct a std::string"
);
…
}
…
};
こんな感じでチェック!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc: In instantiation of 'Person::Person(T&&) [with T = const char32_t (&)[5]; <template-parameter-1-2> = void]':prog.cc:35:18: required from hereprog.cc:16:27: error: no matching function for call to 'std::__cxx11::basic_string<char>::basic_string(const char32_t [5])': name(std::forward<T>(n))
^In file included from /usr/local/gcc-head/include/c++/6.0.0/string:52:0,
from prog.cc:2:/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:535:9: note: candidate: template<class _InputIterator, class> std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(_InputIterator, _InputIterator, const _Alloc&)
basic_string(_InputIterator __beg, _InputIterator __end,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:535:9: note: template argument deduction/substitution failed:prog.cc:16:27: note: candidate expects 3 arguments, 1 provided: name(std::forward<T>(n))
^In file included from /usr/local/gcc-head/include/c++/6.0.0/string:52:0,
from prog.cc:2:/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:512:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(basic_string&& __str, const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:512:7: note: candidate expects 2 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:508:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const basic_string& __str, const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:508:7: note: candidate expects 2 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:504:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::initializer_list<_Tp>, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:504:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'std::initializer_list<char>'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:477:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(basic_string&& __str) noexcept^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:477:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'std::__cxx11::basic_string<char>&&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:465:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, _CharT, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(size_type __n, _CharT __c, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:465:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:455:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:455:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const char*'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:445:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const _CharT* __s, size_type __n,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:445:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:427:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const basic_string& __str, size_type __pos,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:427:7: note: candidate expects 4 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:411:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]
basic_string(const basic_string& __str, size_type __pos,^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:411:7: note: candidate expects 3 arguments, 1 provided/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:399:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const basic_string& __str)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:399:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const std::__cxx11::basic_string<char>&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:391:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _Alloc& __a)^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:391:7: note: no known conversion for argument 1 from 'const char32_t [5]' to 'const std::allocator<char>&'/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:380:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string()^
/usr/local/gcc-head/include/c++/6.0.0/bits/basic_string.h:380:7: note: candidate expects 0 arguments, 1 providedprog.cc:19:3: error: static assertion failed: Parameter n can't be used to construct a std::string
static_assert(^
GCCさん
ここ!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc:16:4: error: no matching constructor for initialization of 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >'): name(std::forward<T>(n))^ ~~~~~~~~~~~~~~~~~~
prog.cc:35:9: note: in instantiation of function template specialization 'Person::Person<char32_t const (&)[5], void>' requested herePerson p(U"name");
^/usr/local/libcxx-head/include/c++/v1/string:1326:40: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const allocator_type' (aka 'const std::__1::allocator<char>') for 1st argument
_LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)^
/usr/local/libcxx-head/include/c++/v1/string:1333:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const std::__1::basic_string<char>' for 1st argumentbasic_string(const basic_string& __str);^
/usr/local/libcxx-head/include/c++/v1/string:1338:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'std::__1::basic_string<char>' for 1st argumentbasic_string(basic_string&& __str)^
/usr/local/libcxx-head/include/c++/v1/string:1348:31: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'const value_type *' (aka 'const char *') for 1st argument_LIBCPP_INLINE_VISIBILITY basic_string(const value_type* __s);
^/usr/local/libcxx-head/include/c++/v1/string:1369:5: note: candidate constructor not viable: no known conversion from 'char32_t const[5]' to 'initializer_list<value_type>' for 1st argument
basic_string(initializer_list<value_type> __il);^
/usr/local/libcxx-head/include/c++/v1/string:1363:9: note: candidate constructor template not viable: requires 2 arguments, but 1 was providedbasic_string(_InputIterator __first, _InputIterator __last);^
/usr/local/libcxx-head/include/c++/v1/string:1366:9: note: candidate constructor template not viable: requires 3 arguments, but 1 was providedbasic_string(_InputIterator __first, _InputIterator __last, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1323:31: note: candidate constructor not viable: requires 0 arguments, but 1 was provided_LIBCPP_INLINE_VISIBILITY basic_string()
^/usr/local/libcxx-head/include/c++/v1/string:1346:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(basic_string&& __str, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1334:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const basic_string& __str, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1350:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const value_type* __s, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1352:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(const value_type* __s, size_type __n);^
/usr/local/libcxx-head/include/c++/v1/string:1356:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(size_type __n, value_type __c);^
/usr/local/libcxx-head/include/c++/v1/string:1371:5: note: candidate constructor not viable: requires 2 arguments, but 1 was providedbasic_string(initializer_list<value_type> __il, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1354:5: note: candidate constructor not viable: requires 3 arguments, but 1 was providedbasic_string(const value_type* __s, size_type __n, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1358:5: note: candidate constructor not viable: requires 3 arguments, but 1 was providedbasic_string(size_type __n, value_type __c, const allocator_type& __a);^
/usr/local/libcxx-head/include/c++/v1/string:1359:5: note: candidate constructor not viable: requires at least 2 arguments, but 1 was providedbasic_string(const basic_string& __str, size_type __pos, size_type __n = npos,^
prog.cc:19:3: error: static_assert failed "Parameter n can't be used to construct a std::string"static_assert(^
Clangさん
ここ!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
でも、今回の例の場合、ホントはそもそも std::enable_if の条件にstd::is_constructible 使えばいいと
思うんだけど…
Errata には、コンパイラのエラーメッセージが、「そんなコンストラクタ無いよ」ってなるので、意味が変わっちゃうって書いてあるけど…
Item 27: Familiarize yourself with alternativesto overloading on universal references.
こんな感じclass Person {public:template<typename T,
typename = std::enable_if_t<std::is_constructible<std::string, T>::value>>explicit Person(T&& n): name(std::forward<T>(n)){…
}
…
};
こんな感じで無効化!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc: In function 'int main()':prog.cc:26:18: error: no matching function for call to 'Person::Person(const char32_t [5])'Person p(U"name");
^prog.cc:18:2: note: candidate: Person::Person(Person&&)Person(Person&&) = default;^
prog.cc:18:2: note: no known conversion for argument 1 from 'const char32_t [5]' to 'Person&&'prog.cc:17:2: note: candidate: Person::Person(const Person&)Person(const Person&) = default;^
prog.cc:17:2: note: no known conversion for argument 1 from 'const char32_t [5]' to 'constPerson&'prog.cc:11:11: note: candidate: template<class T, class> Person::Person(T&&)explicit Person(T&& n)
^prog.cc:11:11: note: template argument deduction/substitution failed:
GCCさん
Item 27: Familiarize yourself with alternativesto overloading on universal references.
prog.cc:26:9: error: no matching constructor for initialization of 'Person'Person p(U"name");
^ ~~~~~~~prog.cc:17:2: note: candidate constructor not viable: no known conversion from 'constchar32_t [5]' to 'const Person' for 1st argument
Person(const Person&) = default;^
prog.cc:18:2: note: candidate constructor not viable: no known conversion from 'constchar32_t [5]' to 'Person' for 1st argument
Person(Person&&) = default;^
/usr/local/libcxx-head/include/c++/v1/type_traits:244:78: note: candidate template ignored: disabled by 'enable_if' [with T = char32_t const (&)[5]]template <bool _Bp, class _Tp = void> using enable_if_t = typename enable_if<_Bp, _Tp>::type;
^
Clangさん
Clang さんすごい!
Item 27: Familiarize yourself with alternativesto overloading on universal references.
覚えておくこと• ユニバーサルリファレンスとオーバーロードの組み合わせの代替案には、異なる関数名を使用する、パラメータをconstへの左辺値参
照で渡す、パラメータを値で渡す、タグディスパッチを使う、が含まれる。
• std::enable_if を通じてテンプレートを制約することで、ユニ
バーサルリファレンスとオーバーロードを同時に使用できるが、それはコンパイラがユニバーサルリファレンスのオーバーロードを使用する条件を制御する。
• ユニバーサルリファレンスはたいてい効率上の利点があるが、概して使い勝手の点で欠点もある。