c++ - 是否可以仅在需要时缩小范围?

标签 c++ guideline-support-library

假设我有以下代码,其中 bar 是在另一个库中定义的:

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); 很可能出现在库的 future 版本中,第一个版本会自动切换到使用它(通过推导规则), 而第二个版本不会。有什么方法可以在 void bar(long long x); 不可用时检测缩小失败,并在发布时切换到仅调用它?

我试过:

  • bar(narrow(y));,但无法推导narrow的模板参数。
  • bar(superint{y}),其中 superint 是一个struct {long long x;},为 long long 重载类型转换运算符> 和 int,后者带有窄检查。到目前为止,这是我最好的想法,因为它在添加 void bar(long long x); 时给出了一个 ambiguous call 编译时错误,让我们知道要调用的地方在适当的时候更新我们的代码库。不过,它看起来不像您会放入 GSL 中的东西,所以我不太满意..

更新

有很多好的答案,但它们都必须按函数而不是按参数来实现。理想情况下,解决方案应该类似于 GSL,您只需对有缩小风险的论点做一些事情。访问者模式在这里可能很有用,我们只需要将 quz(xi,yi,z,w) 重写为 superint_visit(quz, superint{xi},superint{xi}, z,w); 假设 xi 和 yi 是有缩小风险的整数参数。像这样的东西:

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});
}

即使我在这里接受了答案,我仍然希望听到任何能够实现上述目标的人!

最佳答案

您可以利用这样一个事实,即重载解析有利于非模板函数的参数类型完美匹配,而不是函数模板:

#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 long long 将支持非收缩函数模板重载 (C),其主模板已被删除,仅允许在 T 恰好为 long long 时调用>(无转换)通过特化 (C.SP1)。一旦 (B) 处的 void bar(long long); 变得可用,它将被重载决议选为比函数模板候选者更好的可行候选者。

如果您担心在编译库本身时引入额外的 bar 重载(上面的函数模板)可能会破坏 bar 的重载解析,您可以添加一个 public bar 的包装器,并将上面的重载决议委托(delegate)放在定义包装器的 TU 中,通过在未命名的命名空间中声明它来为添加的 bar 重载应用内部链接。例如:

// foo.h
#pragma once
void foo(long long y);

// foo.cpp
#include "foo.h"
#include "gsl/gsl_narrow"
#include "the_lib/bar.h"

namespace {

template <typename T>
void bar(T) = delete;

template<>
void bar<long long>(long long y) { bar(narrow<int>(y)); }

}  // namespace

void foo(long long y) {
    bar(y);
}

关于c++ - 是否可以仅在需要时缩小范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63521155/

相关文章:

c++ - std::shared_ptr 模板化与非模板化复制/移动构造函数

c++ - 打开 .dat 文件时遇到问题

c++ - 为分配的 char 数组赋值失败

c++ - 如何在 C++ 中重用枚举运算符重载?

c++ - 为什么 unordered_map 和 map 提供相同的性能?

c++ - 如何使用 gsl::span 修复 No array to Pointer Decay 警告?

c++ - gsl 库中的 span 和 array_view 有什么区别?

c++ - 我应该如何表示我拥有的连续元素序列?