我编写了一个实用程序函数,该函数允许我将std::vector的元素写入由定界符分隔的std::ostream。 (我知道还有一个关于实现此目标的问题,但是这个问题是关于为什么我的实现无法正常工作。)

template<typename I, typename S>
struct Joiner {
    const I &iterable;
    const S &separator;
};

template<typename I, typename S>
inline auto join(const I &iterable, const S &separator) -> const Joiner<I, S> {
    return Joiner<I, S>{iterable, separator};
}

template<typename I, typename S>
std::ostream &operator<<(std::ostream &stream, const Joiner<I, S> &joiner) {
    auto i = joiner.iterable.begin();
    auto end = joiner.iterable.end();
    if (i != end) {
        stream << *i++;
        while (i != end) {
            stream << joiner.separator << *i++;
        }
    }
    return stream;
}

这里没什么异常。

但是有时候vector中不是我想要输出的东西。例如,它可能是vector<int *>,而我想输出int值,而不是指针。

因此,我认为,与其假定要打印的值为*i,不如让我们传入一个“提取器”,这样,在vector<int *>的情况下,我可以指定**i

我尝试了几种方法,但并没有真正实现。我当前的想法是模板参数X是eXtracted类型,并且join方法应采用返回该类型的std::function。提取器函数的参数类型为I::iterator
template<typename I, typename S, typename X>
struct Joiner {
    const I &iterable;
    const S &separator;
    const std::function<X(typename I::iterator)> extract;
};

template<typename I, typename S, typename X>
inline auto join(const I &iterable, const S &separator, std::function<X(typename I::iterator)> extract) -> const Joiner<I, S, X> {
    return Joiner<I, S, X>{iterable, separator, extract};
}

然后,在operator <<实现中,我使用joiner.extract(i++)而不是*i++

但是,这不起作用。我写了这个测试程序:
int main(int argc, char *argv[]) {
    int a = 5;
    int b = 20;
    int c = 25;
    std::vector<int *> v{&a, &b, &c};
    std::cout << join<std::vector<int *>, std::string, int>(v, ",", [](std::vector<int *>::iterator i) { return **i; }) << std::endl;
}

编译时出现如下错误:
/Users/wboyce/git/mathias/util.hpp:31:19: error: no matching function for call to object of type 'const std::function<int (typename vector<int *, allocator<int *> >::iterator)>' (aka 'const function<int (__wrap_iter<int **>)>')
    stream << joiner.extract(i++);
              ^~~~~~~~~~~~~~
/Users/wboyce/git/mathias/scratch.cpp:19:15: note: in instantiation of function template specialization 'operator<<<std::__1::vector<int *, std::__1::allocator<int *> >, std::__1::basic_string<char>, int>' requested here
    std::cout << join<std::vector<int *>, std::string, int>(v, ",", [](const std::vector<int *>::iterator i) { return **i; }) << std::endl;
              ^
/usr/local/opt/llvm/bin/../include/c++/v1/functional:2255:9: note: candidate function not viable: no known conversion from '__wrap_iter<int *const *>' to '__wrap_iter<int **>' for 1st argument
    _Rp operator()(_ArgTypes...) const;

我认为麻烦的根源是可迭代类型是I,但是我在const I &上使用了它,这意味着迭代器将返回类似const I::iterator的内容,但是这对我来说很模糊。
  • 我不认为迭代器类型确实是const I::iterator,因为那是I上的const迭代器,而不是const I上的非const迭代器。
  • 这是否意味着迭代器将返回const X,还是应该将constness烘焙到X中?
  • 是否可以使编译器将类型参数推断为join,所以我不必指定它们?
  • 我还需要做些什么才能使这项工作?

  • 这甚至是正确的方法吗?最初我以为X将是提取器函数本身的类型...但是我也无法做到这一点。

    最佳答案

    使用std::function的主要原因(或主要原因之一)是在需要类型擦除时。

    这里不需要类型擦除。

    #include <iostream>
    #include <functional>
    #include <vector>
    
    template<typename I, typename S, typename E>
    struct Joiner {
        const I &iterable;
        const S &separator;
        E e;
    };
    
    template<typename I, typename S, typename E>
    inline auto join(const I &iterable, const S &separator,
             E &&e) -> const Joiner<I, S, E> {
        return Joiner<I, S, E>{iterable, separator, std::forward<E>(e)};
    }
    
    template<typename I, typename S, typename E>
    std::ostream &operator<<(std::ostream &stream, const Joiner<I, S, E> &joiner) {
        auto i = joiner.iterable.begin();
        auto end = joiner.iterable.end();
        if (i != end) {
            stream << joiner.e(*i++);
            while (i != end) {
                stream << joiner.separator << joiner.e(*i++);
            }
        }
        return stream;
    }
    
    int main(int argc, char *argv[]) {
        int a = 5;
        int b = 20;
        int c = 25;
        std::vector<int *> v{&a, &b, &c};
    
        std::cout << join(v, ",", [](int *p) { return *p; }) << std::endl;
    
    }
    

    07-24 09:44
    查看更多