2012-02-29 2 views
5

私はちょうどバイナリリテラルoperator ""_bをハックしようとしていましたが、再帰を終了しようとしました。パラメータパックのオーバーロードと競合しない空の明示的なテンプレートパラメータリストを使用して呼び出すことができる関数を定義するにはどうすればよいですか?次に、インスピレーション:何か変なパック拡張に一致します。空のバリューパック展開は、型パックまたはオプションの型パラメータと一致しますか?

しかしGCCは、空の引数リストの存在しない型が、明示的に必要とされない型のパラメータリストの型と一致しないと訴える。この方法で動作するはずですか?

template< char head, char ... tail > 
constexpr unsigned long long parse_binary() { 
    return ((head - '0') << sizeof ... (tail)) 
     + parse_binary< tail ... >(); // Error: no overload for termination. 
} 

template< typename = void > // I want this to match an empty pack of chars. 
// template< short = 0 > // even this would do. 
constexpr unsigned long long parse_binary() { 
    return 0; 
} 

template< char ... digits > 
constexpr unsigned long long operator ""_b() { 
    return parse_binary< digits ... >(); 
} 

#include <iostream> 

int main() { 
    std::cout << 010101_b << '\n'; 
} 

注:質問はoperator ""_bを実装していません。この問題は、パックをパラメータリストに展開し、std::integral_constant型を渡すことで解決できます。

注2:このコードは実際にはマイナー調整で機能します。下の私の答えを参照してください。しかし、それは直接問題に対処していません。うーん、おそらく私はこれに答える代わりにこれを編集したはずです...

+0

あなたは '<...文字、最初の尾をchar型のヘッド、CHAR>元のテンプレート'テンプレートを作成することにより、再帰を終了することができ、かつ終了のために 'template 'を使用してください - しかし、あなたが求めているものではないと思います。 –

+1

@BjörnPollexいいえ、2つの引数を渡すとあいまいです。パックは空にすることができます。 'テンプレート< char head >'と 'テンプレート'はこのトリックを行いますが、それは問題ではありません。 – Potatoswatter

答えて

0

、このようなトリッキーなのコンプライアンスに関する公式の単語2つのオーバーロードが転置されている場合、与えられたコードになります。

2番目の終了オーバーロードは、テンプレート定義時に最初に名前解決されるため、最初のオーバーロードは最初に表示されません。テンプレートパラメータに依存する関数呼び出しのみが、インスタンス化の時間まで検索が延期されます。

ただ、これは作品 、明確にする:一般的に

template< typename = void > // Define this one first! 
constexpr unsigned long long parse_binary() { 
    return 0; 
} 

template< char head, char ... tail > 
constexpr unsigned long long parse_binary() { 
    return ((head - '0') << sizeof ... (tail)) 
     + parse_binary< tail ... >(); // Bingo: overload found. 
} 
+0

iirc in C++ 11テンプレートIDとテンプレート引数のいずれかが依存する場合、関数名が依存するという明示的な箇条書きがあります。今はそれがテンプレートidであることを知るために、コンパイラはまずそれをテンプレートで探す必要がありますが、soesntは2番目のインスタンス化時間の検索を妨げます。 –

+0

これはGCCのバグであることを確認しました。「parse_binary < tail ... >()」は、「parse_binary」のインスタンス化に依存する参照も行う必要があります。 –

+0

@ JohannesSchaub-litb:興味深い!バグを報告しましたか? – Potatoswatter

4

1文字で再帰を終了する方が良いでしょうか?いずれの場合においても

template<char Ch> 
constexpr unsigned long long parse_binary(){ 
    return Ch - '0'; 
}; 

// second head to disambiguate 
template< char head1, char head2, char ... tail > 
constexpr unsigned long long parse_binary() { 
    return ((head1 - '0') << sizeof ... (tail)+1) + parse_binary< head2, tail ... >(); 
} 

は、問題が parse_binary文字ゼロのためにはクランがうまく指摘するように、可変引数バージョンの前に宣言する必要があるということである。

error: call to function 'parse_binary' that is neither visible in 
     the template definition nor found by argument-dependent lookup 

// call trace... 

note: 'parse_binary' should be declared prior to the call site 
     constexpr unsigned long long parse_binary() { 
+0

LOL。私はちょうどBjörnへの私の返事をこれにスクロールする前に書きました。メモの中で述べたように、これはバイナリリテラルを実装するのではなく、パラメータパックに関する質問です。エレガントな解決策は、関数のパラメータリストでパックを展開することです。 – Potatoswatter

+0

ああ、オーバーロードされた名前がテンプレートパラメータに依存するADLでない限り、オーバーロード候補セットを展開することはできません。チャットでのLucのコメントとあわせて、私は今理解していると思う。 – Potatoswatter

関連する問題