将 "extractor" 传递给也采用可迭代对象的 C++ 方法
Passing an "extractor" to a C++ method that also takes an iterable
我编写了一个实用程序函数,允许我将 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
,不如传入一个 "extractor",这样,在 vector<int *>
的情况下,我可以指定 **i
.
我试过几种方法,但效果并不理想。我目前的想法是模板参数 X
是提取类型,而 join
方法应该采用 std::function
那个 returns 那个类型。提取器函数的参数类型为 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
.[=67= 上的非 const 迭代器]
- 这是否意味着迭代器将要返回
const X
还是应该将常量融入 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;
}
我编写了一个实用程序函数,允许我将 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
,不如传入一个 "extractor",这样,在 vector<int *>
的情况下,我可以指定 **i
.
我试过几种方法,但效果并不理想。我目前的想法是模板参数 X
是提取类型,而 join
方法应该采用 std::function
那个 returns 那个类型。提取器函数的参数类型为 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
.[=67= 上的非 const 迭代器] - 这是否意味着迭代器将要返回
const X
还是应该将常量融入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;
}