1

我有一个用于 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()判断Tis的类型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

4 回答 4

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;
}
于 2013-09-09T14:25:55.203 回答
4

如果只有成员函数需要基于类模板类型的特殊实现,那么简单地为成员函数定义一个特化可能比使用 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
于 2013-09-09T14:48:28.287 回答
3

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

用于std::is_same<T, std::string>测试是否Tstd::string.

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

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

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

你不能在非模板上做 SFINAE,所以如果成员函数不是模板函数,你就不能使用 SFINAE。

您的选择包括:

  • 使成员函数成为模板(见很快的答案

  • 转发到另一个模板函数:


    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进行转换。
于 2013-09-09T14:47:02.137 回答
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_;
};
于 2013-09-09T14:31:33.513 回答