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
}

首先 - 您需要特殊的转换基类,对于单个转换,您将在下一个代码片段中看到通过基类函数调用转换会导致“未指定”转换,像这样的 forlong不会编译。

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++ 仍然不支持它们。

第二:转换和覆盖限制现在应该在您的 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始终触发的问题;您需要使其断言表达式依赖于模板参数。

下一个问题是您的模板不会使用 lambda 类型模板参数进行实例化,因为 lambdas(甚至是无捕获 lambdas)不是默认可构造的。您可以通过返回传统函子 ( structwith operator()) 或将 lambda 类型转换为调用 lambda 的默认可构造类型来解决此问题。

一种策略是在调用时间接使用 void 指针,因为无捕获 lambda 不应该关心它们的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);

不过,这仍然是未定义的行为;它与将 lambda 函数指针存储在闭包类型中的标准一致,在这种情况下,这将导致通过 void 指针进行调用。

于 2012-10-25T12:18:10.330 回答