10

我试图以某种方式禁用/标记为已弃用的可怕std::string::operator=(char)重载(根据我的经验,它仅在错误地将整数分配给字符串时使用,并导致微妙且难以跟踪的错误)。

我试过:

  • 带有静态断言的显式特化

    #include <string>
    #include <type_traits>
    
    template<> std::basic_string<char> &std::basic_string<char>::operator=(char c) {
        static_assert(false, "Don't use this!");
    }
    

    它已经失败了,因为<string>它的显式实例化已经失败了std::string

  • 属性,在[[deprecated]]不同位置应用于与上述类似的声明;我尝试的任何立场似乎都没有产生任何合理的结果;
  • =delete,由于与上述类似的原因而失败;
  • 我考虑过使用链接器技巧(以类似的方式,在同一个项目中,我们使用链接器选项对杂散使用进行运行时检查setlocale--wrap ld,但这是一个模板和内联方法这一事实使问题复杂化。

现在的问题:

  • 是否有一种标准方法可以以某种方式禁用(就像会发生的那样=delete)标准库中的任何函数或方法(阅读:在您无法更改标头中的声明的库中)?
  • 如上所述,但不是禁用,而是添加一个警告(就像会发生的那样[[deprecated]]);
  • 未能通过标准方法,是否有特定于 g++ 的东西?
  • 如果没有“通用”(=适用于任何方法、任何类、任何函数……)解决方案,我们是否可以将某些东西应用于这种特定情况(=禁用模板类的方法,甚至可能只是一个具体实例化)?
4

3 回答 3

4

您可以使用以下编译器/链接器选项:

$ g++ -O0 test.cpp -Wl,--wrap=_ZNSsaSEc

解释:

The_ZNSsaSEc是您的违规函数的修饰名称:

$ echo _ZNSsaSEc | c++filt
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(char)

-Wl编译器选项是将选项传递给链接器。

并且--wrap=<symbol>链接器选项将对给定符号的任何引用转换为替代__wrap_<symbol>。而且由于您(希望)没有定义名为 的函数__wrap__ZNSsaSEc,因此您将收到一个不错的链接器错误:

test.cpp:(.text+0x26): undefined reference to `__wrap__ZNSsaSEc'

并且-O0是禁用优化并防止编译器内联函数。正如@SergeBallesta 在评论中指出的那样,如果存在内联,则链接器技巧将不起作用。

也许有点破解,但是,嘿,它的工作原理!

于 2015-11-18T11:14:49.933 回答
1

好吧,恐怕标准库的目的是...... standard,因此不提供允许开发人员调整它的钩子。

一个丑陋的方法(永远不要说建议你使用它;-))将使用标准库头文件只是文本文件的事实,因此您可以在本地开发环境中轻松更改它们。一种可能不太糟糕的方法是设置一个文件夹,其中包含指向原始头文件的链接,但修改后的头文件除外,并指示编译器将该文件夹用于系统头文件。

这样,你可以改变任何你想要的东西,但是......可移植性和可维护性......这真的是一个绝望的解决方案......

于 2015-11-18T11:29:16.877 回答
0

这是 clang++ 特有的,因为我不知道在 gnu 工具链中调用了什么等效功能。这也有点矫枉过正。

Rodrigo 关于使用链接器交换符号的建议非常适合非内联的情况。如果你偶尔在 O0 构建所有东西就足够了。

否则,llvm (clang) 工具链对优化管道提供了惊人的控制量。例如,您可以在不进行优化的情况下进行编译,然后使用 opt 自己运行优化,然后转换为目标文件。

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

opt 工具是可扩展的。老实说,我不能说扩展是微不足道的,但这个过程有据可查。您可以编写一个编译器通道,在看到您的标准库函数时发出警告。最终结果可以调用如下:

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt --load DeprecationPass.so test.bc -o /dev/null
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

如果您确信自定义传递是正确的(不仅有用),您可以使用 opt 的单个调用。可能可以通过 clang 前端传递标志来选择,但如何做到这一点并不是很明显。

总体而言,遵循 rodrigo 的建议并偶尔在 O0 构建整个产品可能是一个更好的计划 - 但令人兴奋的是,clang 允许您做这样的事情。

于 2015-11-19T11:08:46.430 回答