是“lazy man's enable_if”法律C ++? [英] Is the "lazy man's enable_if" legal C++?

查看:152
本文介绍了是“lazy man's enable_if”法律C ++?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常使用一种技术,我称为懒惰的人 enable_if ,其中我使用 decltype 运算符以基于一些模板输入启用功能。这里有一个小例子:

  template< typename F> 
auto foo(F&& f) - > decltype(f(0),void())
{
std :: cout< 1<< std :: endl;
}

template< typename F>
auto foo(F&& f) - > decltype(f(0,1),void())
{
std :: cout< 2< std :: endl;
}

使用 - std = c ++ 11 ,g ++ 4.7+和Clang 3.5+愉快地编译该位的代码(和它的工作原理)。但是,当使用MSVC 14 CTP5时,我得到这个错误抱怨 foo 已经被定义:


错误错误C2995:'未知类型foo(F&&)':函数模板有
已定义c ++ - scratch main.cpp 15




所以我的问题是:懒惰的人的 enable_if 合法的C ++或者这是一个MSVC的错误?

解决方案

[temp.over.link] / 6 指定两个函数模板声明何时是重载。这是通过定义两个函数模板的等价来完成的:


如果两个函数模板[ ..]有返回类型[..],它们使用上述规则
等价,以比较涉及模板参数的表达式。


上述规则是


两个涉及模板参数的表达式被认为是
如果包含表达式的两个函数定义将满足一个定义规则(3.2)[..]


[basic.def.odr] / 6 中,


给定一个在多个翻译
单元中定义的名为 D 的实体,然后




  • D 的每个定义都应由;
  • ; 明确地说,作为返回类型根据 [dcl.fct] / 2 )不由相同的令牌,包含这些表达式的两个函数定义将违反ODR。

    因此, foo 的声明声明非等效函数模板并重载名称。



    您看到的错误是由于缺少VC ++对表达式SFINAE的支持而发出的 - 可能不会检查尾随返回类型的等效性。






    解决方法



    您可以使功能模板以其他方式 - 更改模板参数列表。如果你重写第二个定义,像这样:

      template< typename F,int = 0> 
    auto foo(F&& f) - > decltype(f(0,1),void())
    {
    std :: cout< 2< std :: endl;
    }

    然后VC ++ 编译很好
    我缩短了[temp.over.link] / 6中的引号,包括:


    两个函数模板<如果它们在相同的
    范围中声明,具有相同的名称具有相同的模板参数列表 [..]


    事实上,为了能够轻松引入新的重载,你可以使用一个小帮手:

      template< int I> 
    using overload = std :: integral_constant< int,I> *;

    用法是

      //记得分开>和=与空格
    模板< typename ... F,overload< 0> = nullptr>
    auto foo(F&& ... f) - > decltype(f(0,1)...,void())

    template< typename ... F,overload< 1> = nullptr>
    auto foo(F&& ... f) - > decltype(f(0,1,2)...,void())

    演示


    I frequently use a technique I call the "lazy man's enable_if," where I use decltype and the comma operator to enable a function based on some template input. Here is a small example:

    template <typename F>
    auto foo(F&& f) -> decltype(f(0), void())
    {
        std::cout << "1" << std::endl;
    }
    
    template <typename F>
    auto foo(F&& f) -> decltype(f(0, 1), void())
    {
        std::cout << "2" << std::endl;
    }
    

    With --std=c++11, g++ 4.7+ and Clang 3.5+ happily compile that bit of code (and it works as I would expect). However, when using MSVC 14 CTP5, I get this error complaining of foo already being defined:

    Error error C2995: 'unknown-type foo(F &&)': function template has already been defined c++-scratch main.cpp 15

    So my question is: Is the "lazy man's enable_if" legal C++ or is this an MSVC bug?

    解决方案

    [temp.over.link]/6 specifies when two function template declarations are overloads. That is done by defining equivalency of two function templates as follows:

    Two function templates are equivalent if they [..] have return types [..] that are equivalent using the rules described above to compare expressions involving template parameters.

    The "rules described above" are

    Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule (3.2) [..]

    The ODR relevant for this part states in [basic.def.odr]/6 that

    Given such an entity named D defined in more than one translation unit, then

    • each definition of D shall consist of the same sequence of tokens;

    Clearly, as the return types (which are the trailing return types as per [dcl.fct]/2) do not consist of the same tokens, two function definitions containing those expressions would violate the ODR.
    Hence the declarations of foo declare non-equivalent function templates and overload the name.

    The error you see is issued due to the lack of support from VC++ for expression SFINAE - presumably the trailing-return-types are not inspected for equivalency.


    Workaround

    You can make the function templates non-equivalent in another way - Change the template parameter list. If you rewrite the second definition like so:

    template <typename F, int=0>
    auto foo(F&& f) -> decltype(f(0, 1), void())
    {
        std::cout << "2" << std::endl;
    }
    

    Then VC++ compiles it fine. I shortened the quote in [temp.over.link]/6, which covers this:

    Two function templates are equivalent if they are declared in the same scope, have the same name, have identical template parameter lists [..]

    In fact, to be able to easily introduce new overloads, you can use a little helper:

    template <int I>
    using overload = std::integral_constant<int, I>*;
    

    Usage is e.g.

    // Remember to separate > and = with whitespace
    template <typename... F, overload<0> = nullptr>
    auto foo(F&&... f) -> decltype(f(0, 1)..., void())
    
    template <typename... F, overload<1> = nullptr>
    auto foo(F&&... f) -> decltype(f(0, 1, 2)..., void())
    

    Demo.

    这篇关于是“lazy man's enable_if”法律C ++?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆