应用SFINAE检查是否为T

时间:2015-10-15 08:25:21

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

我目前正在研究一个小型的数学矢量类。 我想要两个向量类Vector2Vector3可以从一个到另一个构造。 例如:

Vector2<float> vec2(18.5f, 32.1f); // x = 18.5; y = 32.1
Vector3<float> vec3(vec2);         // x = 18.5; y = 32.1; z = float()

为了做到这一点,为了简化可扩展性,我想使用特征VectorTraits及其基本定义:

template <typename T>
struct VectorTraits
{
  typedef T                      VectorType;
  typedef typename T::ValueType  ValueType;

  static const unsigned int      dimension = T::dimension;
};

此表单允许用户在现有的Vector类(例如glm :: vec2)和我的类之间建立链接。然后可以从glm :: vec2创建一个Vector2。

此外,这种技术可以让我为使用SFINAE定义VectorTraits的所有类编写通用流操作符。

有我的问题,我无法定义operator<<,因此当VectorTraits不适合给定类型时,这是无提示错误。

这是我的最后一次尝试(Ideone link here):

#include <iostream>
#include <type_traits>

// To define another operator
struct Dummy
{};

// Traits class
template <typename T>
struct VectorTraits
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};

// Fake vector class. Defines the required typedef.
struct Vec
{
  typedef float   ValueType;
  static const std::uint16_t dimension = 2;
};

// Streaming operator for Dummy.
std::ostream& operator<<(std::ostream& stream, const Dummy& d)
{
    stream << "dummy.\n";
    return stream;
}

// Streaming operator attempt for classes defining VectorTraits.
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>
std::ostream& operator<<(std::ostream& stream, const T& vec)
{
    std::cout << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

int main()
{
    std::cout << "Test\n";
    std::cout << Vec();
    std::cout << Dummy();
    return 0;
}

通过此尝试,错误只是

error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'Vec')
prog.cpp:33:15: note: candidate: template<class T, typename std::enable_if<(VectorTraits<T>::dimension > 0), void>::type <anonymous> > std::ostream& operator<<(std::ostream&, const T&)
 std::ostream& operator<<(std::ostream& stream, const T& vec)
               ^
prog.cpp:33:15: note:   template argument deduction/substitution failed:
prog.cpp:41:19: note:   couldn't deduce template parameter '<anonymous>'

如果我改变

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = 0>

我收到了另一个错误

prog.cpp:13:35: error: 'char [21]' is not a class, struct, or union type
   typedef typename T::ValueType   ValueType;

我设法使用的唯一版本是一个空的VectorTraits类,必须专门针对每个Vector。但我也希望提供一种“自动”成为Vector并定义typedef的方法。

我不明白为什么在show版本中,我的运算符不会被编译器保留。我也尝试了一些变体,但它总是要么匹配所有东西,要么什么都不匹配。

4 个答案:

答案 0 :(得分:4)

一个问题是您没有为std::enable_if_t实例化的结果提供默认参数,因此模板参数推断失败。解决此问题的一种方法是向其添加* = nullptr

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)

但是,现在我们收到错误,因为在VectorTraits<T>实例化中,T::ValueType是必需的。这不在SFINAE上下文中,因此如果该成员不存在则会发生硬故障。我们可以通过在模板参数中添加SFINAE检查来解决这个问题:

template <class T, typename = typename T::ValueType, 
          std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)

您可以将其分解为外部IsValidVector检查,以便在需要多次检查时,您可以更新一个点。

答案 1 :(得分:4)

您可以向vectorTrait添加一个图层,只有在它有效时才启用:

public class MainActivity extends Activity {

    EditText eid,ename;
    Button add;
    public DatabaseClass mdb=null;
    public Cursor c=null;
    public SQLiteDatabase db=null;
    public static final String DATABASE_NAME="Sample.db";
    public static final int DATABASE_VERSION=1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        eid=(EditText)findViewById(R.id.rid);
        ename=(EditText)findViewById(R.id.rname);
        add=(Button)findViewById(R.id.radd);        
        mdb=new DatabaseClass(getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);

        add.setOnClickListener(new View.OnClickListener() {         
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                db = mdb.getWritableDatabase();
                ContentValues cv=new ContentValues();
                int id=Integer.parseInt(eid.getText().toString());
                String name=ename.getText().toString();

                cv.put("ID",id );
                cv.put("NAME",name );
                db.insert("DEMO", null, cv);

            }
        }); 

    }
}

database class

public class DatabaseClass extends SQLiteOpenHelper{

    public DatabaseClass(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        db.execSQL("CREATE TABLE DEMO(ID INTEGER,NAME VARCHAR);");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
        // TODO Auto-generated method stub

    }

}

Demo

答案 2 :(得分:1)

由于以下原因,您的特定代码无效。

对于您的向量,std::enable_if_t<(VectorTraits<T>::dimension > 0)>是一些类型。因此,您将operator<<声明为模板,其中第二个参数是该类型的。对于Dummy(和char*),没有这样的类型,因此SFINAE排除了这一点。但是对于Vec,您std::enable_if_t<(VectorTraits<T>::dimension > 0)>类型,并且编译器期望模板参数为该类型的某些。当然,它无法找出该值应该是什么。

答案 3 :(得分:0)

Jarod42提供的解决方案在Visual Studio上不起作用,所以我设法找到另一个。我发布它以便它可以帮助那些遇到这个或那个问题的人:

首先,我声明了类VectorTraits而没有将其定义为:

template <typename T, typename = void>
struct VectorTraits;

我提供的专业化只在T::dimension > 0

时启用
template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};

最后,我使用std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>检查特征是否可用:

template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
    return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

最终的工作示例可用here或以下:

#include <iostream>
#include <type_traits>

// Traits class
template <typename T, typename = void>
struct VectorTraits;

template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};


// Fake vector class. Defines the required typedef.
struct Vec
{
  typedef float   ValueType;
  static const std::uint16_t dimension = 2;
};

// Fake vector class. Defines the required typedef.
struct VecFake
{
};

template <>
struct VectorTraits<VecFake>
{
  typedef VecFake                       VectorType;
  typedef float   ValueType;
  static const std::uint16_t      dimension = 12;
};

// Streaming operator attempt for classes defining VectorTraits.
template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
    return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

int main()
{
    std::cout << "Test\n";  // Test
    std::cout << Vec();     // Traits. Dimension = 2
    std::cout << VecFake(); // Traits. Dimension = 12
}