6

私はこのクラスをVC++で作成しましたが、これは明らかに標準に準拠していません(TBHは、標準で許可されていないので奇妙に見えます)

  1. どうすれば標準のC++11に変換できますか?

コード:

#include <functional>
template <typename T, typename U, typename T_to_U >
class MultiUnitValue  //Multi for now == 2 :)
{
    const T t;
    T_to_U conversion_function;
public:
    MultiUnitValue()    : t(0)
    {}
    MultiUnitValue(T t_): t(t_)
    {}
    template<typename V>
    V in() const
    {
        BOOST_STATIC_ASSERT(0);
        // "you are trying to call in with type(unit) not supported"
    }
    template<>
    T in<T>() const
    {
        return t;
    }
    template<>
    U in<U>() const
    {
        return conversion_function(t);
    }
};

使用法:

auto f = [](int i){return i*2.54;};

MultiUnitValue<int, float,decltype(f)> muv(10);
auto rv = muv.in<float>();
  1. 誤った使用を防ぐためにBOOST_STATIC_ASSERTを配置しました
    が、MSVCは使用されていない場合はインスタンス化されないようです(予想どおり)が、g ++ 4.7に移植しようとすると、テンプレートがインスタンス化されていなくても起動していましたか?static_assertに置き換えても機能しません...

これを回避する方法はありますか?

4

2 に答える 2

5

メンバー関数はクラステンプレートに特化できないため、ソリューションは「標準準拠」ではありませんでした。これは、関数を部分的に特殊化することはできないという一般的な規則によるものです。したがって、メンバー関数テンプレートの「完全な」特殊化でさえ、完全に特殊化されていないクラスのため、実際には部分的な特殊化です。


私の解決策:

C++11バージョン

私のバージョンでのあなたの例、私はこれがあなたが望むものであると信じています:

int main(){
  auto f1 = [](int i){return i*2.54;};
  auto f2 = [](int i){ std::stringstream ss; ss << i; return ss.str(); };
  MultiUnitValue<int, float, std::string>  vv(1, f1, f2);
  std::cout << vv.in<int>() << "\n";
  std::cout << vv.in<float>() << "\n";
  std::cout << vv.in<std::string>() << "\n";
  // std::cout << vv.in<long>() << "\n"; // error to compile
}

まず第一に、特別な変換基本クラスが必要です。単一の変換の場合、次のコードフラグメントで、基本クラス関数を介して変換を呼び出すと、このような「指定されていない」変換longがコンパイルされないことがわかります。

template <class T, class U>
class Conversion {
public:
    Conversion(const std::function<U(const T&)>& f) : f(f) {}
    U convert (const T& v) const { return f(v); }
private:
   std::function<U(const T&)>  f;
};
template <class T>
class Conversion<T,T> {
public:
    T convert (const T& v) const { return v; }
};

そして、可変個引数テンプレートを使用したクラス:

template <class T, class... V> // V... means all desired conversions
class MultiUnitValue : private Conversion<T,T>, private Conversion<T,V>... {
// allowed conversion:         ^^^^^^^^^^^^^^^^^        ^^^^^^^^^^^^^^^^^^
public:
   MultiUnitValue(T v, const std::function<V(const T&)>&... f) : Conversion<T,V>(f)..., v(v) {}

   template <class U>
   U in() const
   {
      // this static assert is not needed - but just to show the message
      static_assert(std::is_base_of<Conversion<T,U>, MultiUnitValue<T,V...>>::value, 
                   "Not allowed conversion");

      // static_assert is not needed 
      // since if you MultiUnitValue does not derive from Conversion<T,U> 
      // - then this call will not compile too
      return this->Conversion<T,U>::convert(v);
   }

private:
   T v;
};

LVSの例: http: //liveworkspace.org/code/05b6ada146cc8f05d027a5536859a087

可変個引数テンプレートのないバージョン:

また、VC ++はまだそれらをサポートしていないため、可変個引数テンプレートなしでソリューションを準備しました。

2番目:変換とカバレッジの制限がT_to_Uタイプに含まれているはずです。

このアプローチでは、C++11バージョンと比較して使用法が少し不便になります。

int main(){
  auto f1 = [](int i){return i*2.54;};
  auto f2 = [](int i){ std::stringstream ss; ss << i; return ss.str(); };
  // next 2 lines differ from C++11 version
  typedef ConvertFunctions2<int, float, std::string> CF_f1_f2; 
  MultiUnitValue<int, CF_f1_f2>  vv(1, CF_f1_f2(f1, f2));
  std::cout << vv.in<int>() << "\n";
  std::cout << vv.in<float>() << "\n";
  std::cout << vv.in<std::string>() << "\n";
  // std::cout << vv.in<long>() << "\n"; // error to compile
}

これMultiUnitValueはあなたの例よりも単純で、私のC ++ 11バージョンからでも単純ですが、class CFはるかに複雑になります。

template <class T, class CF>
class MultiUnitValue {
public:
   MultiUnitValue(T v, const CF& cf) : v(v), cf(cf) {}

   template <class U>
   U in() const
   {
      return cf.Conversion<T,U>::convert(v);
   }

private:
   T v;
   CF cf;
};

単純な「ヘルパー」変換クラスは、C++11バージョンと同じになります。

template <class T, class U>
class Conversion {
   ...
};
template <class T>
class Conversion<T,T> {
   ...
};

そして、VC ++(およびC ++ 03の昔)での可変個引数テンプレートの代替:

template <class T>
class ConvertFunctions0 : public Conversion<T,T> {};

template <class T, class V1>
class ConvertFunctions1 : public Conversion<T,T>, public Conversion<T,V1> {
public:
  ConvertFunctions1(std::function<V1(const T&)> f1) : Conversion<T,V1>(f1) {}
};

template <class T, class V1, class V2>
class ConvertFunctions2 : public Conversion<T,T>, public Conversion<T,V1>, public Conversion<T,V2> {
public:
  ConvertFunctions2(std::function<V1(const T&)> f1, std::function<V2(const T&)> f2) 
  : Conversion<T,V1>(f1), Conversion<T,V2>(f2) 
  {}
};

ご覧のとおりConvertFunctions3、追加ConvertFunctions4はそれほど大きな問題ではありません...

ideoneでの完全な例

于 2012-10-27T23:48:45.790 に答える
1

最初の問題は、クラス内でメンバー関数テンプレートを特殊化することはできず、(テンプレート)クラスの外部でそれらを特殊化することはできないということです。そうすると、それらは部分関数の特殊化になるからです。これを回避する最も簡単な方法は、プライベートメンバー関数のオーバーロードを使用することです。

private:
    T in(T *) const { return t; }
    U in(U *) const { return conversion_function(t); }
    template<typename V> V in(V *) const {
        static_assert((V *)0, "you are trying to call in with type(unit) not supported");
    }
public:
    template<typename V> V in() const { return in((V *)0); }

static_assertこれは、常に起動する問題を修正する方法も示しています。そのアサーション式をテンプレートパラメータに依存させる必要があります。

次の問題は、ラムダ(キャプチャレスラムダでさえ)がデフォルトで構築できないため、テンプレートがラムダタイプのテンプレートパラメータでインスタンス化されないことです。structこれを修正するには、従来のファンクター( with )に戻るかoperator()、ラムダ型をラムダを呼び出すデフォルトの構築可能な型に変換します。

キャプチャレスラムダはポインタが何であるかを気にする必要がないため、1つの戦略は呼び出し時にvoidポインタを間接化することthisです。

template<typename L> struct default_constructible_lambda {
  template<typename... Args> auto operator()(Args &&...args) const
    -> decltype(std::declval<L>()(std::forward<Args>(args)...)) {
      return (*(L *)(0))(std::forward<Args>(args)...); }
};
MultiUnitValue<int, float, default_constructible_lambda<decltype(f)>> muv(10);

ただし、これは未定義の動作です。これは、ラムダの関数ポインターがクロージャー型内に格納される標準と一致しています。この場合、これにより、voidポインターを介して呼び出されます。

于 2012-10-25T12:18:10.330 に答える