c++ 自定义点对象模式。不同命名空间中的参数和定制

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

我最近了解了自定义点对象模式并尝试实现它。起初,它看起来像是一种很好的方法来实现一些基本功能并将其扩展到不同类型。

在下面的示例中,我使用启用了 c++20 的 Visual Studio 2022。

我最终得到了这段代码,与此类似。我使用 std::ranges::swap 作为参考。

namespace feature {
    namespace _feature_impl {
        /* to eliminate lookup for wrong functions */
        template<typename T>
        void use_feature(T&) = delete;

        /* filtering customizations */
        template<typename T>
        concept customization =
            requires(T& reward) {
                { use_feature(reward) };
            };

        struct fn {
            /* allow only if there is customization */
            /* compile-time error otherwise */
            constexpr void operator () (customization auto& reward) const {
                use_feature(reward);
            }
        };
    }

    /* main interface to access feature */
    inline constexpr auto apply = _feature_impl::fn{};
}

按预期工作时的使用示例.

  • 全局命名空间中定义的结构体和自定义函数
/* result: compiles, prints "Foo" */
struct Foo {};

void use_feature(Foo&) {
    std::cout << "Foo\n";
}

auto main(int argc, char const** argv) -> int {
    auto foo = Foo{};
    feature::apply(foo);
    
    return 0;
}
  • 和以前一样,但没有定义定制
/* result: doesn't compile */
struct Foo {};
struct Bar {};

void use_feature(Foo&) {
    std::cout << "Foo\n";
}

auto main(int argc, char const** argv) -> int {
    auto bar = Bar{};
    /* passing an object of type, that is not supported */
    feature::apply(bar);

    return 0;
}
  • 与第一个示例相同,但是结构、自定义函数和特性的使用都在命名空间 bar 中
/* result: compiles, prints "Foo" */
namespace bar {
    struct Foo {};

    void use_feature(Foo&) {
        std::cout << "Foo\n";
    }

    void main() {
        auto foo = Foo{};
        feature::apply(foo);
    }
}

auto main(int argc, char const** argv) -> int {
    bar::main();

    return 0;
}
  • bar::main
    放入
    bar::baz::main
/* result: compiles, prints "Foo" */
namespace bar {
    struct Foo {};

    void use_feature(Foo&) {
        std::cout << "Foo\n";
    }

    namespace baz {
        /* now usage in nested namespace baz */
        void main() {
            auto foo = Foo{};
            feature::apply(foo);
        }
    }
}

auto main(int argc, char const** argv) -> int {
    bar::baz::main();

    return 0;
}

但是有些例子不太明白,为什么它们不起作用.

  • 在不同命名空间中定义的结构和自定义。自定义函数可以清楚的看到定义,但是通过特性接口函数
    feature::apply
    访问时,报错
/* result: doesn't compiles */
/* Error C3889 - call to object of class type 'feature::_feature_impl::fn': no matching call operator found */
struct Foo {};
namespace bar {
    void use_feature(Foo&) {
        std::cout << "Foo\n";
    }

    void main() {
        auto foo = Foo{};
        feature::apply(foo);
    }
}

auto main(int argc, char const** argv) -> int {
    bar::main();

    return 0;
}
  • 为 int 等内置类型定义自定义函数
/* result: doesn't compiles */
/* Error C3889 - call to object of class type 'feature::_feature_impl::fn': no matching call operator found */
void use_feature(int&) {
    std::cout << "Foo\n";
}

auto main(int argc, char const** argv) -> int {
    auto i = int{0};
    feature::apply(i);

    return 0;
}

可能是我在第一个不工作的示例中遗漏了一些范围解析规则,但即使这样也不能解释为什么它不适用于具有任何命名空间组合的内置类型。 std::ranges::swap 做同样的事情。这意味着,例如,如果我需要为某种类型添加自定义,我需要将它放在定义此类的同一命名空间中。

假设,在标准库中没有

swap(std::string&, std::string&)
,或者我出于某种原因需要替换它,我应该做这样的事情。

namespace std {
    void swap(std::string&, std::string&) {
        std::cout << "Foo\n";
    }
}

auto main(int argc, char const** argv) -> int {
    auto s = std::string{};
    std::ranges::swap(s, s);

    return 0;
}

我觉得不对。

最初我认为函数

use_feature
的查找会延迟到
feature::apply
调用,因为
feature::apply::operator()
是一个函数模板,并且在这个函数中调用
use_feature
使用模板参数。它看起来像是一种简单灵活的方式来扩展不同类型的功能。但是比起我实现它,尝试在不同的命名空间中移动部分并尝试使用不同的类型......

在我看来,自定义函数

use_feature
将在当前名称空间或更高名称空间中查找是合乎逻辑的。

c++ namespaces customization name-lookup customization-point
1个回答
0
投票

第一个非编译示例失败,因为

use_feature()
是要由ADL 找到的。然而,这要求函数在与其参数相同的命名空间中声明。您的
use_feature(Foo&)
是在 nested 命名空间
bar
中声明的,因此 ADL 不考虑它。

第二个例子失败,因为ADL不适用于基本类型,所以在

fn::operator()
中通过重载决策找到的函数是删除的
use_feature
函数模板。

您可以通过在命名空间

use_feature(int&)
中声明
_feature_impl

来解决这个问题
© www.soinside.com 2019 - 2024. All rights reserved.