为什么或何时将模板引入其命名空间?

如何解决为什么或何时将模板引入其命名空间?

这是一个例子:

#define MAKE_IT_WORK false
namespace Bob {  // Bob's project namespace
   struct DeviceFrequency {};
   extern void debugf(const char* fmt,...);
}  // namespace Bob

namespace SSN {  // Super-Secret Namespace
   namespace debugging {
     extern int  ssn_debug;
     extern void debugf(const char* fmt,...);
   }  // namespace debugging
}  // namespace SSN

namespace SSN::signals { // Super-Secret Namespace,project signals
   template<typename Coder> // In the example,this imports Bob's namespace
   class Frequency {
     public:
     Frequency( void )
     { using namespace ::SSN::debugging; // Why isn't this enough??
       using ::SSN::debugging::debugf;   // Or this??
       if( ssn_debug )
       #if MAKE_IT_WORK
         ::SSN::debugging::  // How can a developer predict that this is needed??
       #endif
         debugf("Frequency(%p,%zd)::Frequency\n",this,sizeof(*this));
     }
   }; // class Frequency
}  // namespace SSN::signals

struct Controller {
   SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
   Controller( void ) : bobcon() {}
}; // class Controller

在此示例中,Bob复制了debugf函数,因为他不想将整个SSN名称空间带入自己的私有名称空间,并且不想每次使用它时都被完全限定使用它它。

SSN开发人员没有意识到模板也可以导入其名称空间(直到它发生),显然使ADL查找发挥作用。尽管ADL查找指出在名称空间中使用using语句会被忽略,但是为什么它完全起作用没有意义。对于一个名称空间无意识地污染另一个名称空间来说似乎太容易了,而预测内联代码中哪一天可能发生的地方太难了。

看起来(至少)在命名空间中使用模板时,每个命名空间函数引用必须完全合格,因为使用模板时,您无法预测何时可能发生名称冲突。这个对吗?如果是这样,是否有任何方法可以避免似乎需要输入所有多余的名称限定符?这次曝光是否仅限于模板,还是所有导入的内联代码都同样容易受到攻击?

使用gcc版本10.2.0和10.2.1(Red Hat 10.2.1-5)编译(Dirty.cpp),我收到以下消息:

make dirty 
c++  -o Dirty.o -c S/Dirty.cpp  -D_CC_GCC -D_OS_BSD -D_HW_X86 -D_OS_LINUX -IS -IH -g -O3 -finline->functions -std=gnu++17 -Wall -Wextra -Wmissing-declarations -Wswitch-default -Werror
S/Dirty.cpp: In instantiation of ‘SSN::signals::Frequency<Coder>::Frequency() [with Coder = Bob::DeviceFrequency]’:
S/Dirty.cpp:146:32:   required from here
S/Dirty.cpp:139:12: error: call of overloaded ‘debugf(const char [30],SSN::signals::Frequency<Bob::DeviceFrequency>*,long unsigned int)’ is ambiguous
 139 |      debugf("Frequency(%p,sizeof(*this));
     |      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
S/Dirty.cpp:124:13: note: candidate: ‘void SSN::debugging::debugf(const char*,...)’
 124 | extern void debugf(const char* fmt,...);
     |             ^~~~~~
S/Dirty.cpp:117:13: note: candidate: ‘void Bob::debugf(const char*,...)’
 117 | extern void debugf(const char* fmt,...);
     |

(编辑:该示例已还原为原始格式,现在已由@ 1201ProgramAlarm重新格式化。出现一个修改后的版本,可以在下面的部分答案中测试示例。)

解决方法

对于模板,ADL包括与该模板关联的名称空间和实体 为模板类型参数提供的模板参数的类型。由于对debugf的调用将this作为参数,因此ADL将包含namespace Bob,因为模板是用Bob::DeviceFrequency实例化的。

,

更新示例:

// Last change: 2020/10/30 11:15 EDT
#define MAKE_IT_WORK true // Modified (Example compiles)
namespace Bob {  // Bob's project namespace
   extern double thing;     // (Added for answer testing)
   struct Bar { int foo; }; // (Added for answer testing)
   struct Foo { int bar; }; // (Added for answer testing)
   extern Foo bar;          // (Added for answer testing)
   struct DeviceFrequency { double f; }; // (Modified: added "double f;")
   extern void debugf(const char* fmt,...);
   static inline void function(DeviceFrequency& E)  // (??TRICKY inference??)
     { debugf("default function,E: %f\n",E); }
}  // namespace Bob

namespace SSN {  // Super-Secret Namespace
   namespace debugging {
     extern int  ssn_debug;
     extern void debugf(const char* fmt,...);
   }  // namespace debugging
}  // namespace SSN

namespace SSN::signals { // Super-Secret Namespace,project signals
   template<typename Coder>
   class Frequency {
     // Note: The Function type references the template indirectly,and has
     // no purpose other than to make sure that a <Coder> function handler
     // receives a <Coder&> parameter. ADL doesn't care. It brings in the
     // Coder's namespace looking for functions to override.
     typedef std::function<void(Coder&)> Function; // (Added) Another "gotcha"
     const Function function; // This indirectly references Coder&,thus Bob

     double thing= 543.21;   // (Added) Duplicates name in namespace Bob
     double f= 123.45;       // (Added) Duplicates name in Bob::DeviceFrequency
     Bob::Foo bar{876};      // (Added) Uses Bob namespace
     struct Bar { int foo= 0x0F00; } foo; // (Added) Duplicates a Bob struct name
     struct Derived : public Bob::Foo {}; // (Added) Uses Bob namespace
     Derived secret{{671}};  // (Added) Uses Bob namespace in derived object
     struct Container { int n= 888; Bob::Bar m{999}; }; // (Added) Uses Bob namespace
     Container content;      // (Added) Uses Bob namespace in container object

     public:
     // This constructor added to demonstrate indirect template references
     Frequency( const Function& _function ) : function(_function)
     { using namespace ::SSN::debugging; // Without this,Bob::debugf is used
       ::SSN::debugging::    // Disambiguate
       debugf("Frequency::Frequency(Coder: %p,%zd)\n",&_function,sizeof(_function));

       // Drive the function,mostly just to see that it's been coded correctly
       // Note the existence of function(DeviceFrequency& E) in namespace Bob.
       // This *looks* like it might call Bob::function,but it doesn't,// likely because function is an object containing an operator(),a
       // std::function container.
       Coder coder{2468}; function(coder);  // (??TRICKY?? Not ADL ambiguous)
       Coder rodoc{8642}; _function(rodoc); // (??TRICKY?? Not ADL ambiguous)
       Bob::function(coder); // Just to verify it's different
     }

     Frequency( void )       // Default constructor
     { using namespace ::SSN::debugging; // Why isn't this enough??
       using ::SSN::debugging::debugf;  // Or this??
       // Answer: When ADL comes into play,using statements are ignored.
       //         Also,without the first using,Bob::debugf would be used.
       //    (Rationale unclear,but that's the way it is.)
       if( ssn_debug )
         #if MAKE_IT_WORK
           // As explained by @1201ProgramAlarm,it's the 'this' parameter that
           // brought Bob's namespace into play for ADL name resolution.
           //    (Rationale unclear,but that's the way it is.)
           ::SSN::debugging::  // How can a developer predict that this is needed??
         #endif
         debugf("Frequency(%p,%zd)::Frequency\n",this,sizeof(*this));

       // All remaining lines in Frequency added for detailed testing ============
       const void* const that= this; // Another work-around
       debugf("Frequency(%p,that,sizeof(*this)); // (Works)

       Coder coder{3.14};    // (This is a Bob::DeviceFrequency)
       debugf("Coder.f: %f\n",coder.f); // (No ambiguity)
       Bob::debugf("Coder: %f\n",coder); // *** AMBIGUOUS debugf ***

       debugf("f: %f\n",f); // (No ambiguity)
       debugf("thing: %f\n",thing); // (No ambiguity)
       debugf("Bob::thing: %f\n",Bob::thing); // (No ambiguity)
       debugf("bar.bar: %d\n",bar.bar); // (No ambiguity)
       SSN::debugging::debugf("bar: %d\n",bar); // *** AMBIGUOUS debugf ***
       Bob::debugf("this->bar: %d\n",this->bar); // *** AMBIGUOUS debugf ***
       debugf("foo.foo: 0x%3x\n",foo.foo); // (No ambiguity)
       debugf("this->foo: 0x%3x\n",this->foo); // (No ambiguity)
       debugf("Bob::bar.bar: %d\n",Bob::bar.bar); // (No ambiguity)
       Bob::debugf("Bob::bar: %d\n",Bob::bar); // *** AMBIGUOUS debugf ***
       debugf("secret.bar: %d\n",secret.bar); // (No ambiguity)
       Bob::debugf("secret: %d\n",secret); // *** AMBIGUOUS debugf ***
       debugf("content: %d\n",content); // (No ambiguity)
       Bob::debugf("content.m: %d\n",content.m); // *** AMBIGUOUS debugf ***
       // End of added lines =====================================================
     }
   }; // class Frequency

   template<typename Coder>
   struct Ugly_fix {         // Macros stop ADL lookup,remove usings
      #define debugf ::SSN::debugging::debugf
      #define ssn_debug ::SSN::debugging::ssn_debug

      Ugly_fix( void )       // Default constructor
      {  if( ssn_debug ) debugf("Ugly_fix(%p)::Ugly_fix\n",this);
         debugf("Bob::bar: %d\n",Bob::bar);
         // Bob::debugf("Bob::bar: %d\n",Bob::bar); // Syntax error
      }

      #undef debugf          // Macros (MUST BE) private
      #undef ssn_debug
   }; // struct Ugly_fix

   class Non_template { public:
     Non_template( void )
     { using namespace ::SSN::debugging;
       if( ssn_debug )
         debugf("Non_template(%p,%zd)::Non_template\n",sizeof(*this));
     }
   }; // class Non_template
}  // namespace SSN::signals

// Namespace: **NONE**
static struct Handler { // Note: This actually *USES* ADL to find debugf
   void operator()(Bob::DeviceFrequency& E) { debugf("E: %f\n",E); }
}  handler; // struct Handler

struct Controller {
   SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
   SSN::signals::Frequency<Bob::DeviceFrequency> robcon;
   SSN::signals::Ugly_fix<Bob::DeviceFrequency>  ugly;
   SSN::signals::Non_template non_template;

   // Note that both Frequency constructors are used.
   Controller( void ) : bobcon(),robcon(handler),ugly(),non_template() {}
}; // class Controller

// IMPLEMENTATION ============================================================
namespace SSN {
   int debugging::ssn_debug= true;
   namespace debugging {
     void debugf(const char* fmt,...) {
       printf("SSN: ");      // (For output disambiguation)
       va_list argptr; va_start(argptr,fmt); vprintf(fmt,argptr); va_end(argptr);
     }  // debugf
   }  // namespace debugging
}  // namespace SSN

namespace Bob {
   double thing= 1122.33;    // (Added for answer testing)
   Foo    bar{732};          // (Added for answer testing)
   void debugf(const char* fmt,...) {
     printf("Bob: ");        // (For output disambiguation)
     va_list argptr; va_start(argptr,argptr); va_end(argptr);
   }  // debugf
}  // namespace Bob

// TEST ======================================================================
static inline int            // Number of errors encountered
   test_namespace_glitch( void ) // Test namespace glitch
{
   int errorCount= 0;        // Number of errors encountered

   printf("\ntest_namespace_glitch\n");
   Controller controller;    // (Actually drive the constructor)

   if( true ) {
     errorCount++;           // *Why* does 'this' import namespace?
     printf("Still (partially) unexplained\n");
   }

   return errorCount;
}

(对于那些喜欢运行代码的人,大多数测试包装器也都包括在这里。您只需要一个调用测试的主程序。)

以下是我需要注意的事项:

  • this用作模板代码内部的(裸)参数。 (感谢@1201ProgramAlarm指出了此“功能”。)很容易意外地做到这一点,但很难弄清它为什么会发生。
  • 引用(不受控制的)命名空间对象(即结构或类)用作函数的参数,无论它是否在模板代码中。这很难做到。
  • 此引用可以是间接引用,例如Frequency(const Function&)构造函数中的引用,因此更容易意外使用。

这可能是(也可能不是)ADL“陷阱”的完整列表。 https://en.cppreference.com/w/cpp/language/adl中有很多示例让我头晕目眩,但工作的人通常似乎并不是一旦被ADL咬伤就可能会意外地做这些事情。

我认为,没有明确的意图就不应发生ADL。需要一些语言关键字或信号,例如[[adl]]std::swap(obj1,obj2)来激活它。我想那只是我。

我不知道或不了解ADL设计决策背后的基本原理,即忽略使用语句,而将裸 this 参数视为对模板对象的引用。我确定(希望,希望)它们存在。您可能会对安德鲁·科尼格(Andrew Koenig)的简短评论A Personal Note About Argument-Dependent Lookup感兴趣,该评论是在深入探讨cppreference.com中ADL的奥秘时发现的。

为了便于记录,我在投票并接受@1201ProgramAlarm的答案,因为他解释说 this 参数使ADL发挥了作用。当明确寻找它时,我没有找到任何文档说明会发生这种情况。它没有回答我的问题,“如何防止这种情况发生?”没有它,我就无法开始。

有用的是,您收到一个看似无法解释的“歧义参考”错误消息,让您知道一些奇怪而糟糕的事情。对于必须解决此问题的人员而言,为时已晚,也太糟糕了。对于那些不考虑ADL的人来说太糟糕了,可能不得不花大量的时间首先尝试找出问题的根源,甚至到最后再不得不向stackoverflow寻求帮助,因为ADL太奇怪了。em>

我有一个丑陋的方法来绕过ADL,如Ugly_fix构造函数所示。通过使用(ugh)宏自动神奇地完全限定名称,不仅可以防止ADL,而且不再需要using语句。这种宏必须是临时的,使用后未定义,以避免污染宏名称空间。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-