使用sfinae在clase模板中选择不同的方法实现

时间:2013-09-09 14:03:29

标签: c++ templates c++11 sfinae

我有一个简单的GTK GUI包装类,如下所示:

template <class T>
class LabeledEntry
{
    string name;
    T var;

    Gtk::HBox hbox;
    Gtk::Label label;
    Gtk::Entry entry;

    public:
    LabeledEntry( string _name, T _var, Gtk::VBox* vbox):
        name( _name ),
        var(_var)
    {
        label.set_text( name.c_str() );
        ostringstream os;
        os << var ;

        entry.set_text( os.str().c_str() );
        hbox.add( label );
        hbox.add( entry );

        vbox->add( hbox);
    }

    T Get()
    {
        string valString( entry.get_text());
        istringstream is( valString);
        is >> noskipws >> var;

        return var;
    }
};

如果T Get()的类型为T,我需要string的特殊实现,因为跳过字符串的空格不起作用。所以我在方法中需要getline

我找到了很多std::is__xxx模板来检查很多属性,例如is_integral等等。但是我需要直接与给定类型进行比较。有机会吗?

如何在类中编写这两个实现的语法?类似的东西:

class ...
{ 
    std::enable_if( true, XXX_IS_STRING)::type Get()
    {
    }

    std::enable_if ( false, XXX_IS_SRING)::type Get()
    {
    }
};

对不起,我在成员参数列表中使用没有模板参数的SFINAE时有点困惑。

4 个答案:

答案 0 :(得分:4)

您应该使Get函数模板化并使用std::enable_if,如下所示:

#include <type_traits>
#include <iostream>
#include <string>

template <class T>
class LabeledEntry
{
    // ...
public:
    template <class U = T>
    typename std::enable_if<std::is_same<U, std::string>::value, U>::type
        Get()
    {
        return {"string"};
    }

    template <class U = T>
    typename std::enable_if<!std::is_same<U, std::string>::value, U>::type
        Get()
    {
        return {42};
    }
};

int main()
{
    LabeledEntry<std::string> sle;
    std::cout << sle.Get() << std::endl;

    LabeledEntry<int> ile;
    std::cout << ile.Get() << std::endl;
    return 0;
}

答案 1 :(得分:3)

  

但我需要直接与给定类型进行比较。有机会吗?

使用std::is_same<T, std::string>来测试T是否为std::string

您可以定义别名模板以简化:

  template<typename T>
    using is_string = std::is_same<T, std::string>;
  

对不起,我在成员参数列表中使用没有模板参数的SFINAE时有点困惑。

您无法在非模板上执行SFINAE,因此如果成员函数不是模板函数,则您无法使用SFINAE。

您的选择包括:

  • 使成员函数成为模板(请参阅soon's answer

  • 转发给另一个模板:


    T Get() const
    { return Get_impl( entry.get_text() ); }

  private:
    template<typename P>
      using enable_if = typename std::enable_if<P::value>::type;

    template<typename U, typename Requires = enable_if<is_string<U>>>
      U Get_impl( const U& u ) const
      { ... }

    template<typename U, typename Requires = enable_if<!is_string<U>>>
      U Get_impl( const U& u ) const
      { ... }
  • 或定义LabeledEntry<std::string>的模板专精,或LabeledEntry用来进行转换的其他类的模板专精。

答案 2 :(得分:3)

如果只有成员函数需要基于类模板类型的特殊实现,那么简单地定义成员函数的特化而不是使用SFINAE可能更容易。

#include <iostream>
#include <string>

template <class T>
class LabeledEntry
{
  // ...
public:
  T Get()
  {
    return 42;
  }
};

template <>
std::string LabeledEntry<std::string>::Get()
{
  return "string";
}

int main()
{
  std::cout << LabeledEntry<std::string>().Get() << "\n"
            << LabeledEntry<int>().Get() << std::endl;
}

这导致以下输出:

string
42

答案 3 :(得分:2)

这样做的一种方法是在课堂之外使用您委派作品的仿函数。这是一个简单的例子,没有做任何有用的事情,但你应该得到图片:

template <typename U>
struct get_functor
{
    std::string operator()(U const & val) const
    {
        std::stringstream ss;
        ss << val;
        return ss.str();
    }
};

template <>
struct get_functor<std::string>
{
    std::string operator()(std::string const &) const
    {
        return "something else";
    }
};

template <typename T>
struct demo
{
    explicit demo(T val) : val_(val) {}

    decltype(get_functor<T>()(std::declval<T>())) get() const
    {
        return get_functor<T>()(val_);
    }

    private:
        T const val_;
};

或者你可以使用enable_if,但在那里你必须创建一个虚拟模板typename U = T来启用SFINAE:

enum class enable_if_helper {};

template <bool C> 
using enable_if = typename std::enable_if<C, enable_if_helper>::type;

template <bool C>
using disable_if = typename std::enable_if<!C, enable_if_helper>::type;

template <typename T>
struct demo
{
    explicit demo(T val) : val_(val) {}

    template <
        typename U = T, 
        enable_if<std::is_same<U, std::string>::value>...
    >
    std::string get() const
    {
        return "something else"; 
    }

    template <
        typename U = T,
        disable_if<std::is_same<U, std::string>::value>...
    >
    std::string get() const
    {
        std::stringstream ss;
        ss << val_;
        return ss.str();
    }

    private:
        T const val_;
};