【问题标题】:Template specialization within namespace命名空间内的模板特化
【发布时间】:2019-07-15 04:08:57
【问题描述】:

当这种奇怪的行为发生时,我正在处理一些代码:

在第一个测试中,基本模板函数、用户和模板特化位于同一个命名空间中,并且它的行为与我预期的一样:

    namespace Test1
    {
        template <typename V, typename T>
        int doFoo(V& a_visitor, T& a_value)
        {
            return 0;
        }

        struct Foo
        {
            template <typename T>
            int process(T const& a_value)
            {
                return doFoo(*this, a_value);
            }
        };

        template <typename T>
        int doFoo(Foo& a_vis, T const& a_ptr)
        {
            return 1;
        }
    }

    int main()
    {
        int const k{ 42 };

        return Test1::Foo{}.process(k); // returns 1
    }

但是当我将基本模板化函数及其特化移到另一个命名空间中时,会选择基本函数:

namespace Test2
{
    namespace b
    {
        template <typename V, typename T>
        int doBar(V& a_visitor, T& a_value)
        {
            return 0;
        }
    }

    struct Bar
    {
        template <typename T>
        int process(T const& a_value)
        {
            return b::doBar(*this, a_value);
        }
    };

    namespace b
    {
        template <typename T>
        int doBar(Bar& a_vis, T const& a_ptr)
        {
            return 1;
        }
    }
}

int main()
{
    int const k{ 17 };

    return Test2::Bar{}.process(k); // returns 0
}

编辑我可以做的更奇怪:在示例 1 中,如果我用 Test1::doFoo 替换对 doFoo 的调用,我会再次遇到错误的行为!

谁能解释一下这里发生了什么?如果我真的需要 struct Bar 不在命名空间 b 内,我该怎么办?

【问题讨论】:

    标签: c++ templates namespaces template-specialization


    【解决方案1】:

    首先,这些不是特化,而是重载。完全不同且互不相关的函数模板。

    您看到的行为与argument-dependent lookup 一致。当遇到不合格函数调用时,编译器通过检查与函数调用的每个参数关联的命名空间来构建重载集。

    通常这不会在“之后”找到声明,但模板是特殊的。在模板中查找依赖名称,例如依赖于模板参数(a_value 的类型)的函数调用,是在模板实例化后执行的,而不是在定义点。这发生在main,在命名空间完成并且所有重载都可用之后,ADL 找到第二个重载。

    这也是为什么当您通过Test1 限定调用时,您不再发现第二个重载。这否定了 ADL,并且只允许出现在调用点之前的重载。解决它的最简单方法可能是延迟 process 的定义,直到所有重载都可用,正如其他答案所示。

    【讨论】:

    • 谢谢!我认为 ADL 是这里的关键。这就是说在你的第三段之后我有点困惑:Test2 中的过程仍然是模板化的,因此它不应该找到重载,因为实例化点仍然在 oberload 的声明之后? ps:我在第 4 段迷路了,不确定您是否指的是我的 Edit
    • @VictorDrouin - ADL 仍然发生,但与 Bar 关联的命名空间中没有 doBarb 未关联)。所以 ADL 不会向集合中添加任何重载。
    • 哦……好吧,我想我明白了。 ADL + 模板化用户的结合使之成为可能。好吧,我想我会放弃我的命名空间之一;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-17
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    • 1970-01-01
    • 2015-08-04
    相关资源
    最近更新 更多