2017-09-25 92 views
5

我想std :: enable_if第一次和挣扎。 任何指导将不胜感激。enable_if与复制构造函数

当作玩具例子,这里是一个简单的静态矢量类,为此我要定义一个拷贝构造函数,但行为取决于载体的相对大小:

  1. 只是将数据复制到更小或相同大小的矢量
  2. 复制数据到一个较大的载体中,然后垫用零

其余部分,以便所述载体类是:

template <size_t _Size> 
class Vector 
{ 
    double _data[_Size]; 

public: 
    Vector() 
    { 
     std::fill(_data, _data + _Size, 0.0); 
    } 

    const double* data() const 
    { 
     return _data; 
    } 
... 
}; 

拷贝构造函数应该支持这样的事情,第3版第2个元素复制到V2:

Vector<3> v3; 
Vector<2> v2(v3); 

我试过行为1.像这样的拷贝构造函数,编译:

template <size_t _OtherSize, 
    typename = typename std::enable_if_t<_Size <= _OtherSize>> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

,但编译器无法将其与行为2区分开来,即使enable_if条件是互斥的。

template <size_t _OtherSize, 
    typename = typename std::enable_if_t<_OtherSize < _Size>> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _OtherSize, _data); 
     std::fill(_data + _OtherSize, _data + _Size, 0.0); 
    } 

我也试图把enable_if的说法相反,但它不能推断_OtherSize的价值:

template <size_t _OtherSize> 
    Vector(const typename std::enable_if_t<_Size <= _OtherSize, 
    Vector<_OtherSize>> & v) 
    : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

什么是做到这一点(使用enable_if,不是一个简单的最佳方式如果声明)?

感谢

+4

根据定义,复制构造函数不能是模板。你可以有一个模板化的构造函数来复制,但它仍然不会是一个复制构造函数。 ; - ] – ildjarn

+1

这不是问题,但是以下划线开头的名称后跟大写字母('_Size','_OtherSize')和包含两个连续下划线的名称将保留供实施使用。不要在你的代码中使用它们。 –

+1

不相关,但在'std :: enable_if_t'之前不需要'typename'。 – Oktalist

回答

7

忽略默认的这些构造的签名是

template <size_t N, typename> 
Vector(const Vector<N>&) 

也就是说,它们最终是相同的。

一来区分它们的方法是使模板参数类型直接依赖于enable_if的条件:

template <size_t _OtherSize, 
    std::enable_if_t<(_Size <= _OtherSize), int> = 0> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

template <size_t _OtherSize, 
    std::enable_if_t<(_OtherSize < _Size), int> = 0> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _OtherSize, _data); 
     std::fill(_data + _OtherSize, _data + _Size, 0.0); 
    } 

顺便说一句,像_Size_OtherSize名是留给实施,因此用户代码非法 - 丢失下划线和/或大写字母。

此外,正如@StoryTeller暗示的那样,当编译器生成的拷贝构造函数具有理想行为时,您不希望第一个构造函数适用于_OtherSize == _Size。所述构造函数对于相同大小的Vector s已经没有专门的复制构造函数了,所以在重载过程中不会被选中,但最好通过将<=切换为<来使其清晰。

+1

对你的评论,'<='是有问题的,应该真的是'<'。如果只是为了防止在调用“错误”c'tor时出现混淆。 – StoryTeller

+2

@StoryTeller:你的意思是这样的构造函数不适用,而不是编译器生成的copy-c'tor?是的,可能,但我试图限制对代码的语言问题的回答,而不是逻辑问题(OP未提及)。虽然好点,但我会记下它 - 谢谢! – ildjarn

+2

永远不会在重载解析期间应用。没有模棱两可的风险,除了OP想知道为什么他们的断点未被命中:) – StoryTeller

4

请勿使用_Cap;它们被保留用于执行。事实上,标准来源使用了tgese名称,因为它们是保留的。不要模仿std/system头的内部命名约定。

template <size_t O> 
Vector(const Vector<O>& v) : Vector() 
{ 
    constexpr auto to_copy = (std::min)(O, Size); 
    constexpr auto to_fill = Size-to_copy; 
    auto const* src=v.data(); 
    std::copy(src, src + to_copy, _data); 
    std::fill(_data + to_copy, _data + to_copy+to_fill, 0.0); 
} 
Vector(const Vector& v) = default; 

你会发现这优化到你想要的代码;在没有填充的情况下,std::fill(foo, foo, 0.0)一起被调用,并且thr体是一个循环,当通过同一个指针两次时,易于证明是编译器的noop。

+0

您是否在编写'(std :: min)',以便将它解析为表达式并且将std :: min'视为非限定ID?如果是,为什么? – YSC

+3

@YSC:避免windows.h宏shenanigans是一个猜测。 – ildjarn