forward_list:分配(_InputIterator __first,_InputIterator __last)/分配(size_type __n,const _Tp& __val)

人气:158 发布:2022-10-16 标签: c++ c++11 forward-list

问题描述

我已经实现了 forward_list 的子集,并想测试方法 assign(size_type __n,const _Tp& __val)但出现编译器错误,因为编译器想调用方法 assign(_InputIterator __first,_InputIterator __last)

I have implemented a subset of the forward_list and wanted to test the method assign(size_type __n, const _Tp& __val) but I get a compiler error because the compiler wants to call the method assign(_InputIterator __first, _InputIterator __last) instead.

我写了以下代码片段,以说明问题:

I have written the following snippet, just to illustrate the problem:

test.h

#ifndef TEST_H
#define TEST_H

#include <utility> // Just to get the std::size_t

template<typename _Tp>
class forward_list {
  public:
    typedef std::size_t size_type;

    void assign(size_type n, const _Tp& val)
    {
      printf("%s\n", __PRETTY_FUNCTION__);
    }

    template<typename _InputIterator>
    void assign(_InputIterator first, _InputIterator last)
    {
      printf("%s\n", __PRETTY_FUNCTION__);
    }
};

#endif // TEST_H

test.cpp

#include <stdlib.h>
#include <stdio.h>
#include "test.h"

int main()
{
  forward_list<int> l;
  l.assign(10, 5);
  return 0;
}

执行的输出为:

void forward_list<_Tp>::assign(_InputIterator, _InputIterator) [with _InputIterator = int; _Tp = int]

我想使用方法 assign(size_type __n ,称为const _Tp& __val)

编译器版本(以防万一): g ++(Debian 4.7) .2-5)4.7.2

Compiler version (just in case it matters): g++ (Debian 4.7.2-5) 4.7.2

我使用的签名与 std中使用的签名类似: forward_list ,以及以下代码段(使用STL):

I have used similar signatures to the signatures used in the std::forward_list and, with the following code snippet (using the STL):

std::forward_list<int> l;
l.assign(10, 5);

编译器知道必须调用 assign(size_type __n,const _Tp& ; __val),不要感到困惑。我想念什么?

The compiler knows that it has to call assign(size_type __n, const _Tp& __val) and doesn't get confused. What am I missing?

推荐答案

调用 l.assign(10,5); ,有两个可行的重载:

When you call l.assign(10, 5);, there are two viable overloads:

void assign(size_type n, const int& val)

template <>
void assign(int first, int last)

当我们说非模板函数时优先于模板函数,这只有在两者的转换顺序无法区分时才成立。但是在这种情况下,函数模板将完全匹配(您的两个参数均为 int ,无需转换),而非模板则必须进行提升(必须将 10 int 升级到 size_t )。因此,这就是首选函数模板重载的原因。

When we say that non-template functions are preferred to template functions, that is only true if the two have indistinguishable conversion sequences. But in this case, the function template will match exactly (both of your arguments are int, no conversion necessary), while the non-template will have to undergo promotation (have to promote 10 from int to size_t). So that's why the function template overload is preferred.

关于如何修复它,您只需要使模板 not 成为可行的重载即可。 。这涉及为输入迭代器编写type_trait,使用 void_t 并不困难:

As to how to fix it, you just need to make the template not a viable overload. That involves writing a type_trait for input iterator, which using void_t is not hard:

template <typename... >
using void_t = void;

template <typename T, typename = void>
struct is_input_iterator : std::false_type { };

template <typename T>
struct is_input_iterator<T, void_t<
    decltype(std::declval<T>() == std::declval<T>()),
    decltype(std::declval<T>() != std::declval<T>()),
    decltype(*std::declval<T>()),
    decltype(++std::declval<T>()),
    decltype(std::declval<T>()++)
    >> : std::true_type { };

然后要求 is_input_iterator

template <typename _InputIterator,
          typename = std::enable_if_t<is_input_iterator<_InputIterator>::value>>
void assign(_InputIterator first, _InputIterator last);

还有很多其他方式可以做这种事情,我碰巧喜欢 void_t 。无论采用哪种方式,都必须确保该模板根本不可行。

There are lots of other ways to do this sort of thing, I just happen to like void_t. Regardless of which way you do it, you have to ensure that the template simply isn't viable.

570