小编Ste*_*ger的帖子

使用显式命名空间限定符时,模板实例化行为会发生变化吗?

我一直在试验可组合管道的系统,该系统涉及一组“阶段”,这些阶段可以进行模板化。每个阶段都处理自己的设置,执行和清除,模板推导用于构建管道使用的“状态”的最小列表。这需要很多样板模板代码,这些代码显示出一些明显不协调的行为。尽管进行了成功的实验,但实际上将其滚动到我们的代码库中仍会由于无效实例化而导致错误。

花了一些时间来找出玩具(工作)解决方案与功能更丰富的版本之间的区别,但最终将其缩小为一个明确的命名空间规范。

template<typename KeyType = bool>
struct bind_stage
{
    static_assert(!std::is_same<KeyType, bool>::value, "Nope, someone default instantiated me");
};

template<typename BoundStage, typename DefaultStage>
struct test_binding {};

template<template<typename...>class StageTemplate, typename S, typename T>
struct test_binding <StageTemplate<S>, StageTemplate<T>> {};

template<typename T>
auto empty_function(T b) {}
Run Code Online (Sandbox Code Playgroud)

然后我们的主要:

int main()
{
    auto binder = test_binding<bind_stage<int>, bind_stage<>>();
    //empty_function(binder); // Fails to compile
    ::empty_function(binder); // Compiles happily
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在,我不确定是否会失败。一方面,我们创建了一个test_binder<bind_stage<int>,bind_stage<bool>>显然包含无效实例化bind_stage<bool>作为其类型定义一部分的a。哪个应该无法编译。

另一方面,它仅作为名称而不是定义包含在内。在这种情况下,它可能只是一个前向声明的模板,并且我们希望它可以工作,只要外部模板中没有任何东西专门引用它即可。

我没有想到的是两种不同的行为,具体取决于我是否添加了(理论上是多余的)全局名称空间说明符。

我已经在Visual Studio,Clang和GCC中尝试了此代码。所有人都有相同的行为,这使我摆脱了编译器错误。这种行为是由C ++标准中的某些原因解释的吗?


编辑:Daniel Langr的另一个例子对我来说意义不大:

template <typename T>
struct X { …
Run Code Online (Sandbox Code Playgroud)

c++ templates instantiation language-lawyer template-argument-deduction

7
推荐指数
1
解决办法
137
查看次数

使用 const-cast 通过非常量引用来延长临时的生命周期

这是最近出现的事情,我觉得它显然不应该起作用:

#include <iostream>
#include <memory>

int main()
{
    std::shared_ptr<int>& ptr = const_cast<std::shared_ptr<int>&>(
        static_cast<const std::shared_ptr<int>&>(
            std::shared_ptr<int>(
                new int(5), [](int* p) {std::cout << "Deleting!"; *p = 999;  delete(p); }
            )
        )
    );
    std::cout << "I'm using a non-const ref to a temp! " << *ptr << " ";
}
Run Code Online (Sandbox Code Playgroud)

shared_ptr此处不需要使用,但自定义删除器允许轻松演示结果对象的生命周期。Visual Studio、Clang 和 GCC 的结果输出是相同的:

我正在使用非常量引用来表示临时值!5 删除!

这意味着结果的生命周期shared_ptr已通过某种机制扩展到匹配std::shared_ptr<int>& ptr.

发生了什么?

现在,我知道在常量引用的情况下,临时对象的生命周期将扩展到引用的生命周期。但是唯一的命名对象是非常量引用,我希望所有其他中间表示的生命周期仅等于初始化表达式。

此外,Microsoft 有一个扩展,它允许非常量引用延长绑定临时的生命周期,但即使禁用该扩展,这种行为似乎也存在,此外,也出现在 Clang 和 GCC 中。

根据这个答案,我相信临时对象被隐式创建为const,因此尝试修改引用的对象ptr可能是未定义的行为,但我不确定这些知识是否告诉我有关为什么延长生命周期的任何信息。我的理解是,这是修改UB 的 const的行为,而不是简单地对它进行非常量引用。 …

c++ const-cast object-lifetime language-lawyer

6
推荐指数
1
解决办法
155
查看次数