【发布时间】:2014-08-06 20:58:16
【问题描述】:
我正在开展一个项目,该项目涉及为用户提供一个界面,以找到任意数量参数的函数的最优值。在内部,所有机制都是围绕std::tuples 的参数类型构建的。不过,我想为用户提供在以“通常”样式编写的函数(例如示例中的f1)上调用我的优化例程的能力,而不是必须将要优化的函数编写为@987654324 的函数@ 实例化(例如示例中的f2)。
作为该机制的一部分,我编写了一个 apply 函数,它将一个元组解包到给定函数的参数中并调用它。
我还创建了一对函数模板,一个通过 lambda 包装器转发给另一个,为优化例程提供接口。简化版本显示为tuple_array_map。目的是提供 SFINAE 以在两者之间进行选择,具体取决于函数类型是可以使用元组参数调用,还是可以使用解压缩的元组成员作为参数调用。为此,我使用带有 SFINAE 触发默认参数的虚拟模板参数。
此方案在 g++ 4.7 及更高版本下完美运行,使用-std=c++11 -pedantic -Wall -Wextra -Werror 编译不会产生警告或错误。
但是,当尝试使用 -std=c++11 在 clang 5.1 下编译时(对不起,我不是一个大的 clang 用户,我不知道是否有更合适的选项集),我得到以下输出示例代码:
clang_fail.cpp:91:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map(f2, tuples);
^~~~~~~~~~~~~~~
clang_fail.cpp:59:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:71:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map([&](const typename TupleArr::value_type& t) {
^~~~~~~~~~~~~~~
clang_fail.cpp:90:5: note: in instantiation of function template specialization
'tuple_array_map<double (*)(double),
std::__1::array<std::__1::tuple<double>, 5>, double, void>' requested here
tuple_array_map(f1, tuples);
^
clang_fail.cpp:59:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
真正令人费解的部分是它似乎从应该 SFINAE 输出的调用表达式中推断出 double 返回,除非我错过了标准中关于模板默认参数或 SFINAE 本身的某些内容。
示例如下 --- 在触发相同行为的同时,我可以得到它的最小限度:
#include <tuple>
#include <array>
#include <utility>
#include <type_traits>
double f1(double x)
{
return x * 2;
}
double f2(const std::tuple<double>& x)
{
return std::get<0>(x) * 2;
}
template<std::size_t N>
struct apply_impl {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&& t, TParams&&... args)
-> decltype(
apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
))
{
return apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
);
}
};
template<>
struct apply_impl<0> {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&&, TParams&&... args)
-> decltype(std::forward<F>(fn)(std::forward<TParams>(args)...))
{
return std::forward<F>(fn)(std::forward<TParams>(args)...);
}
};
template<class F, class Tuple>
auto apply(F&& fn, Tuple&& t)
-> decltype(apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t)))
{
return apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t));
}
template<class Fn, class TupleArr,
class = decltype(std::declval<Fn>()(
std::declval<typename TupleArr::value_type>()))>
void tuple_array_map(Fn f, const TupleArr& arr)
{
for (auto i = 0; i < arr.size(); ++i)
static_cast<void>(f(arr[i]));
}
template<class Fn, class TupleArr,
class = decltype(apply(std::declval<Fn>(),
std::declval<typename TupleArr::value_type>())),
class = void>
void tuple_array_map(Fn f, const TupleArr& arr)
{
tuple_array_map([&](const typename TupleArr::value_type& t) {
return apply(f, t);
}, arr);
}
int main()
{
std::array<std::tuple<double>, 5> tuples = {
std::make_tuple(1),
std::make_tuple(2),
std::make_tuple(3),
std::make_tuple(4),
std::make_tuple(5)
};
// "apply" unpacks a tuple into arguments to a function
apply(f1, tuples[0]);
// this call produces an ambiguity one level down under clang
tuple_array_map(f1, tuples);
// this call directly produces an ambiguity under clang
tuple_array_map(f2, tuples);
}
【问题讨论】:
-
是的,但错误似乎在
libc++;使用clang++-3.5和 gcc-4.8 的libstdc++编译没有错误。 -
我会走得更远,冒着问题出在
std::arraybylibc++的实施中的风险...
标签: c++ templates c++11 clang sfinae