如何解决是否只有在需要时才可以缩小?
void bar(int x); /* this declaration comes from some included header file */
void foo(long long y)
{
return bar(y);
}
为了使故障可检测,GSL指示我如下使用gsl::narrow
:
void bar(int x); /* this declaration comes from some included header file */
void foo(long long y)
{
return bar(narrow<int>(y));
}
我的问题是,假设我知道void bar(long long x);
可能会出现在库的将来版本中,则第一个版本将自动切换为使用它(根据推论规则),而第二个版本则不会。在void bar(long long x);
不可用时,有什么方法可以检测到缩小失败,并在释放时切换到仅调用它?
我尝试过:
-
bar(narrow(y));
,但是无法推导出狭窄的模板参数。 -
bar(superint{y})
,其中superint是struct {long long x;}
和long long
都带有重载类型转换运算符的int
,后者的检查范围很窄。到目前为止,这是我最好的主意,因为在添加ambiguous call
时会出现void bar(long long x);
编译时错误,使我们知道在适当的时候更新代码库的地方。虽然看起来不像您要在GSL中放入的内容,所以我不太满意。.
更新
有很多好的答案,但是都必须按功能而不是参数来实现。理想情况下,解决方案应类似于GSL,在其中您可以对可能会缩小的参数进行一些处理。访客模式在这里可能很有用,假设xi和yi是有缩小风险的整数参数,我们只需要将quz(xi,yi,z,w)
重写为superint_visit(quz,superint{xi},w);
。像这样:
struct superint
{
long long x;
operator long long() { return x; }
operator int() { return narrow<int>(x); }
};
template <typename F,typename... Args>
auto superint_visit(F func,Args&&... args)
{
/* if replacing 'superint' with 'long long' in Args
gives a function that is declared,call that function
(using static_cast<long long> on all 'superint' args
to resolve ambiguity). Otherwise,use static_cast<int>
on all 'superint' args and call that function instead. */
}
以上所有内容都可以存在于gsl
名称空间中,而我只需要将函数编写为:
void foo(long long x)
{
return superint_visit(bar,superint{x});
}
即使我在这里接受了答案,我仍然想听听任何能使上述事情发生的人!
解决方法
您不拥有bar
吗?没问题。为decltype
获取x
只是一个问题。您可以通过获取整个函数的decltype
并编写一个template
来恢复参数类型来做到这一点:
template <typename>
struct argType;
template <typename R,typename A>
struct argType<R(A)>
{
using type = A;
};
void foo(long long y)
{
bar(narrow<typename argType<decltype(bar)>::type>(y));
}
,
您可以利用以下事实:与函数模板相比,重载解析有利于非模板函数的完美参数类型匹配:
#include <iostream>
// (A)
void bar(int y) { std::cout << "bar(int)\n"; }
// (B)
//void bar(long long) { std::cout << "bar(long long)\n"; }
// (C)
template <typename T>
void bar(T) = delete;
// (C.SP1)
template<>
void bar<long long>(long long y) { bar(narrow<int>(y)); }
int main() {
long long a = 12LL;
bar(a); // bar(int)
// bar(long long) if `bar(long long)` above is available.
}
如果void bar(long long);
不可用,则对类型为bar(a)
的参数a
的{{1}}的任何调用将有利于非缩小函数模板重载(C),其重载主模板已删除,仅允许通过专业化(C.SP1)在long long
恰好是T
(无转换)的情况下调用。一旦(B)处的long long
可用,通过重载解析,它将被选择为比功能模板候选者更好的可行候选者。
如果您担心在库本身编译时引入额外的void bar(long long);
重载(上面的函数模板)可能会破坏bar
的重载解析,则可以为{{1}添加一个公共包装器},并将重载决议委托放到上面定义包装器的TU中,通过在未命名的命名空间中声明它来为添加的bar
重载应用内部链接。例如:
bar
,
可以使用boost::callable_traits::args完成第一个参数的类型,这将为您提供 具有所有参数类型的std::tuple,然后可以使用std::tuple_element
获取第一个参数的类型#include <boost/callable_traits/args.hpp>
void bar(int y);
void foo(long long y)
{
bar(narrow<std::tuple_element<0,boost::callable_traits::args_t<decltype(bar)>>::type>(y));
}
,
已经有非常好的创意解决方案发布到该问题。但是它们都有依赖于库实现的问题-例如。如果存在栏重载,则客户端代码不会编译。
从我的角度来看,在这种情况下,bar的客户真正想要的是:
“我想使用接受一个特定整数参数的条,如果存在其他重载则不相关”。
与库无关的非内部直接方法也是最简单的方法-带有整数参数的细条包装器。这样,它与任何库实现细节无关:
void bar(int x); /* this declaration comes from some included header file */
template <typename T,typename = std::enable_if_v<std::is_integral_v<T>>>
inline void mybar(T x)
{
// express directly which bar overload you want to use here
bar(narrow<int>(x)); // <- the only place you need to ajudst if bar interface changes,btw. use the overload from bar which you really want
}
void foo(long long y)
{
return mybar(y); // use mybar instead of bar within your code
}
如果存在int和long的bar重载,那么您可以通过专门设置自己的mybar来区分这两者。
如果条形库界面发生更改,则客户端应无论如何添加并重新编译。您真正想要的是将这些更改保持在中心位置。
我认为您或多或少已经回答了自己的问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。