如何在编译时检查是否存在接受给定参数类型的全局范围函数?

问题描述 投票:0回答:1

我(我认为)需要什么

如何定义一个类型特征来检查对于类型

T
是否声明了函数
::foo(T)

我发现很难的是以 SFINAE 友好的方式拥有

::foo
。例如,如果编译器已达到定义以下内容的程度,

template<typename T>
void f(T t) { foo(t); }

如果到目前为止没有看到任何

foo
,那就完全没问题了。

但是一旦我将

foo
更改为
::foo
,就会出现严重错误。

用例(如果您认为我不需要上述内容)

我有一个这样的定制点:

// this is in Foo.hpp
namespace foos {
inline constexpr struct Foo {
    template <typename T, std::enable_if_t<AdlFooable<std::decay_t<T>>::value, int> = 0>
    constexpr bool operator()(T const& x) const {
        return foo(x);
    }
} foo{};
}

允许通过为所需类型定义 ADL

foos::foo
重载来自定义对
foo
的调用行为。

特质的定义很简单:

// this is in Foo.hpp
template <typename T, typename = void>
struct AdlFooable : std::false_type {};

template <typename T>
struct AdlFooable<T, std::void_t<decltype(foo(std::declval<T const&>()))>>
    : std::true_type {};

鉴于此,如果有人定义

// this is in Bar.hpp
namespace bar {
    struct Bar {};
    bool foo(bar::Bar);
}

然后致电

// other includes
#include "Foo.hpp"
#include "Bar.hpp"
bool b = foos::foo(bar::Bar{});

按预期工作,

foos::foo
将呼叫路由至通过 ADL 找到的
bar::foo(bar::Bar)
。无论两个
#include
的顺序如何,这都可以很好地工作,这很好,因为如果
// other includes
传递地包含它们,则它们可能包含在任一顺序中。

到目前为止一切顺利。

我真正不喜欢这种方法的是,人们可能会错误地定义以下内容,而不是上面的第二个片段,

// this is in Bar.hpp
namespace bar {
    struct Bar {};
}
bool foo(bar::Bar);

foo
在全球范围内。

在这种情况下,如果

#include "Bar.hpp"
恰好出现在
#include "Foo.hpp"
之前,则代码程序将起作用,因为
Foo::operator()
的主体将通过普通查找来选择
::foo(bar::Bar)

但是一旦

#include
的顺序恰好颠倒,代码就会崩溃。

是的,这个错误在于在全局命名空间中定义

foo(bar::Bar)
,但我认为这也是一个错误,它可以纯粹偶然地被忽视。

这就是为什么我想更改类型特征来表达“找到对

foo(T)
的不合格调用,但不是通过普通查找,或者,更直接地说,
foo(std::declval<T>())
必须编译,但是
::foo(std::declval<T>())
必须编译”

c++ c++17 sfinae argument-dependent-lookup global-scope
1个回答
0
投票

执行此操作的普通方法是通过 ADL only 调用自定义实现。因此,无论包含顺序如何,在任何情况下都不会考虑重载

::foo(bar::Bar)

做到这一点的方法是添加毒丸:

namespace foos {
namespace detail {
void foo() = delete;
struct Foo {
    template <typename T, std::enable_if_t<AdlFooable<std::decay_t<T>>::value, int> = 0>
    constexpr bool operator()(T const& x) const {
        return foo(x);
    }
};
}
inline constexpr detail::Foo foo{};
}

普通查找

foo
会找到
foos::detail::foo
,这是行不通的。它无法检查全局范围。因此,
foo
调用将仅通过ADL查找。

© www.soinside.com 2019 - 2024. All rights reserved.