質問
可変なテンプレートを用いたclass A
を定義します.
そのclass A
をテンプレート引数として受け付ける関数foo
を定義します.
このfooのtemplate引数をclass A
のみを受けつけるように制約したいです.
cpp
1 2template<typename ...T> 3class A {}; 4 5template<typename T>//このTはclass Aしか受け付けないようにしたい.class A<ここは何でも大丈夫> 6void foo() { 7 8} 9 10int main() { 11 foo<A<>>();//ok 12 foo<A<int>>()//ok 13 foo<A<int,int,int>>();//ok class Aであれば引数は何でもok 14 //foo<int>();//これはerrorにしたい.fooのテンプレート引数Tがclass Aではないため. 15} 16 17
試したこと
私が考えたのは次のような感じです.
まず,名前空間detail
を作り用意に名前空間にアクセスされないようにします.
そしてclass A
にclass Empty
をpublicで継承させます.
cpp
1 2namespace detail { 3 class Empty { 4 5 }; 6} 7 8 9template<typename ...T> 10class A : public detail::Empty { 11 12}; 13 14
次に関数foo
にてT
がclass Empty
と継承関係にあるかをチェックします.
cpp
1 2template<typename T> 3void foo() { 4 static_assert(std::derived_from<T,detail::Empty>,"関数fooのtemplate引数ははclass Aのみ受け付けます."); 5 6} 7 8///もしくは 9template<typename T> 10concept DerivedFromEmpty = std::derived_from<T,detail::Empty>; 11 12template<DerivedFromEmpty T> 13void foo() { 14 15}
こうすることによって,多少の抜け穴はあるものの目的が達成されました.
cpp
1 2 3namespace detail { 4 class Empty { 5 6 }; 7} 8 9 10template<typename ...T> 11class A : public detail::Empty { 12 13}; 14 15template<typename T> 16void foo() { 17 static_assert(std::derived_from<T, detail::Empty>, "関数fooのtemplate引数ははclass Aのみ受け付けます."); 18 19} 20 21int main() { 22 foo<A<>>(); 23 //foo<int>();//error 24} 25 26 27
さらにEmptyのコンストラクタをprotected
にするとclass Empty
を直接呼び出すミスもなくせるとは思います.(また,コピーコンストラクタやムーブコンストラクタを削除)
また,template<template<typename ...T> typename U>
このあたりの技術でなんとかしようと思ったのですが,無理でした.
問題点
できたはできたのですが,懸念点がいくつかありもっと簡潔でスマートな方法があるのではと考えてしまいます.
↓懸念点
- あまり美しくない(あくまで主観ですが)
もっと次のコードのように簡潔に記述したいです.
cpp
1 2//こんな感じや 3template<typename T> 4void foo(){ 5 static_assert(is_ほにゃらら_v<T>); 6} 7 8//こんな感じ(実際には文法エラー) 9template<typename ...U> 10template<A<...> T> 11void foo(){ 12 13}
- Emptyクラスを使いまわせないので,他に同様の制約をかけようとするともう一個のEmptyクラスを定義する必要がでてきます.
cpp
1 2namespace detail { 3 class Empty { 4 5 }; 6 7 class Empty2 { 8 9 }; 10} 11 12 13template<typename ...T> 14class A : public detail::Empty {}; 15 16template<typename ...T> 17class B : public detail::Empty2 {}; 18 19template<typename T> 20void foo() { 21 static_assert(std::derived_from<T, detail::Empty>, "関数fooのtemplate引数ははclass Aのみ受け付けます."); 22} 23 24template<typename T> 25void foo2() { 26 static_assert(std::derived_from<T, detail::Empty2>, "関数foo2のtemplate引数ははclass Bのみ受け付けます."); 27} 28
- 冗長
上と同じです.
- 少しコストがかかる(かもしれない)
基底クラスが空であれば最適化で基底クラスのメモリは0になると思われますが,基底クラス(class Empty
)のコンストラクタの呼び出しに少しコストがかかるのではないでしょうか(ほんの少しですが)
指定する環境
C++11以上C++20以下でお願いいたします.
その他
- 情報の追記が必要であればコメント欄に書いてください.
- 初心者なのであまり難しい内容はわかりません.平易な解説だと助かります.
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/04/21 11:59