相关疑难解决方法(0)

为什么(成员)函数指针在Visual C++中表现得如此奇怪?

我有一个非常奇怪的问题,我已经简化为以下测试用例:

#include <iostream>
#include <map>
#include <string>

struct Test
{
    std::map<std::string, void (Test::*)()> m;
    Test()
    {
        this->m["test1"] = &Test::test1;
        this->m["test2"] = &Test::test2;
    }
    void test1() { }
    void test2() { }
    void dispatch(std::string s)
    {
        if (this->m.at(s) == &Test::test1)
        { std::cout << "test1 will be called..." << std::endl; }
        else if (this->m.at(s) == &Test::test2)
        { std::cout << "test2 will be called..." << std::endl; }
        (this->*this->m.at(s))();
    }
};

int main()
{
    Test t;
    t.dispatch("test1");
    t.dispatch("test2");
}
Run Code Online (Sandbox Code Playgroud)

它输出

test1将被调用...
test1将被调用...

当启用优化时,这真的很奇怪.这是怎么回事?

c++ linker visual-c++

29
推荐指数
3
解决办法
1039
查看次数

有没有办法为函数指针比较生成警告?

我花了很长时间才发现我的代码中有一个错误被触发/OPT:ICF:

因为/ OPT:ICF可以将相同的地址分配给不同的函数或只读数据成员(使用/ Gy编译的const变量),它可以破坏依赖于函数或只读数据成员的唯一地址的程序.

(我一直在存储和比较函数指针的相等性,当链接器丢弃相同的函数时会断开这些指针.)

现在我需要找到我可能做过这样事情的每一个地方.

测试用例当然是微不足道的:

//MSVC: /Gy /link /OPT:ICF
int test1(void) { return 0; }
int test2(void) { return 0; }
int main(void) { return test1 == test2; }
Run Code Online (Sandbox Code Playgroud)

我试过-Wall,-Wextra,-Weverything,-pedantic,等,但他们没有产生警告.

是否有任何编译器选项或工具(无论是Visual C++,GCC,Clang还是其他部分)可以分析我的代码并告诉我在哪里比较函数指针,就像在上面的代码中一样?

c c++ function-pointers compiler-warnings comdat-folding

29
推荐指数
1
解决办法
800
查看次数

不同的 Lambda 可以衰减到同一个函数指针吗?

两个不同的 lambda(没有捕获,并且具有相同的参数和主体)可以衰减到相同的函数指针吗?

我有这个代码:

#include <cassert>
#include <type_traits>

int main() {
    auto f0 = [](auto x) { return x; };
    auto f1 = [](auto x) { return x; };
    
    static_assert(not std::is_same_v<decltype(f0), decltype(f1)>);

    // MSVC Release-mode combines the functions so the pointers are the same (even though the types should be different.
    assert(static_cast<int(*)(int)>(f0) != static_cast<int(*)(int)>(f1));
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/P3vc45654

我相信static_assert一定会通过。这样就assert保证能通过吗?(我看到 MSVC 在发布模式下assert在我的计算机上失败。)

c++ lambda language-lawyer c++11

27
推荐指数
1
解决办法
1656
查看次数

是否允许实现在同一地址站点两个相同的函数定义?

C++标准说明了关于等于运算符的以下内容==:

[C++11: 5.10/1]: [..]相同类型的两个指针比较相等,当且仅当它们都为空时,都指向相同的函数,或者两者都表示相同的地址.

我的初步解释是函数本身在语义上没有"地址"本身,因此"或两者代表相同的地址"只能用于引用对象,而不是函数.否则为什么要打扰"指向同一个功能"条款呢?

也就是说,当且仅当两者都指向相同的函数period时,相同类型的两个函数指针才相等.

这样做的结果是在这个问题中看到的行为(指向两个不同但相同的函数的指针具有相同的值)将是一个实现错误,因为指向不同函数的指针将是唯一的.

觉得,这是该条款的意图,但我不能找到一种方法来客观地捍卫的观点,这是何等的通道的意思实际上应该推断,或者它真的是委员会的意图,现在我的解释已经成为问题:

[D]跟我说"[...]或两者代表同一个地址." 不满足于Visual C++的行为.(@jstine)

所以我的问题是关于这个标准段落的意图.

或者:

  • 我在正确的轨道上:函数指针必须比较相等,如果它们都指向相同的函数("地址"被诅咒),或者

  • 通道中有一个冗余:函数指针必须比较相等,如果它们都指向同一个函数或两者都代表相同的地址; 并且,通过扩展,允许实现使两个函数存在于同一地址.

这是什么?

c++ language-lawyer

25
推荐指数
1
解决办法
717
查看次数

在/ OPT:ICF存在的情况下,Visual Studio 2013是否正确优化?

我希望以下程序一直返回0.但是,对于Visual Studio 2013(Update 4),程序在发布版本中退出1.我不确定这是一个错误,还是编译器的优化器是正确的,并且依赖于某些边缘行为.如果关闭CONST宏,则release exe返回0.如果优化器确实正确,我是否可以获得允许它发出代码的原因?

#if 1
#   define CONST const
#else
#   define CONST
#endif


class TypeId {
public:
    bool operator== (TypeId const & other) const
    {
        return id == other.id;
    }

private:
    TypeId (void const * id)
        : id(id)
    {}

public:
    template <typename T>
    static TypeId Get ()
    {
        static char CONST uniqueMemLoc = 0;
        return TypeId(&uniqueMemLoc);
    }

private:
    void const * id;
};


int main(int, char **)
{
    typedef int A;
    typedef unsigned int B;

    if (TypeId::Get<A>() …
Run Code Online (Sandbox Code Playgroud)

c++ optimization c++11 visual-studio-2013

24
推荐指数
2
解决办法
1341
查看次数

std :: any没有RTTI,它是如何工作的?

如果我想使用std::any我可以使用RTTI关闭.以下示例-fno-rtti使用gcc 编译并按预期运行.

int main()
{   
    std::any x;
    x=9.9;
    std::cout << std::any_cast<double>(x) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

但是如何std::any存储类型信息?正如我所看到的,如果我std::any_cast使用"错误"类型调用,我会得到std::bad_any_cast预期的异常.

这是如何实现的,或者这可能只是一个gcc功能?

我发现boost::any也不需要RTTI,但我发现也没有解决这个问题.boost :: any需要RTTI吗?.

深入研究STL标题本身并没有给我答案.那段代码对我来说几乎是不可读的.

c++ stl rtti c++17

24
推荐指数
2
解决办法
4104
查看次数

MSVC中的ODR错误?

打印此程序1 1而不是1 2使用MSVC编译时(直到VS 2015).

f1.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 1; } };
    return F();
}

std::function<int ()> f1() { return helper(); }
Run Code Online (Sandbox Code Playgroud)

f2.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 2; } };
    return F();
}

std::function<int ()> f2() { return helper(); }
Run Code Online (Sandbox Code Playgroud)

main.cpp中:

#include <functional>
#include <iostream>

std::function<int ()> f1();
std::function<int ()> f2();

int main() {
    std::cout << …
Run Code Online (Sandbox Code Playgroud)

c++ one-definition-rule visual-c++

20
推荐指数
1
解决办法
325
查看次数

允许在constexpr函数中进行函数指针比较吗?

假设我有一个constexpr函数指针数组,我想编写一个constexpr函数来查找指定函数的数组索引.

我可能有这样的代码:

void test1(){}void test2(){}void test3(){}void test4(){}

typedef void(*func)(void);
constexpr func funcs[] = { &test1, &test2, &test3 };

constexpr int FindMatchingIdx (const func work, const int idx) {
    return (work == funcs[idx]) ? (idx) : (FindMatchingIdx(work, idx + 1));
}

constexpr unsigned int loc = FindMatchingIdx (&test1,0);
Run Code Online (Sandbox Code Playgroud)

现在这个代码在Clang和MSVC上编译,但是GCC只会FindMatchingIdx在使用数组中的第一个元素调用时进行编译.如果FindMatchingIdx调用test1,GCC将编译代码,但如果FindMatchingIdx调用,test2test3GCC将无法编译代码,给出错误消息:

错误:'(test1!= test2)'不是常量表达式.

如果FindMatchingIdx必须递归,GCC将无法将其视为constexpr功能.这是GCC中的错误吗?函数指针比较如何在constexpr函数内部工作?显然,它不能使用由链接器分配的实际指针值.

工作示例:https://godbolt.org/g/xfv1PM

c++ language-lawyer constexpr c++11 c++14

13
推荐指数
1
解决办法
447
查看次数

指向函数和ODR的指针

关于ODR有很多问题,但是我找不到我要找的东西,所以如果这个副本或标题不合适就道歉.

考虑以下:

struct t {t(*id)();};

template<typename T>
t type() {return {type<T>};}
Run Code Online (Sandbox Code Playgroud)

这是我对每种类型定义唯一标识符的过度简化,希望在不同的编译单元中保持唯一.

特别地,给定一个具体的类型Tstd::string,并且假定两个不同的编译单元包括在头文件上面的代码,我想表达

type<T>().id
Run Code Online (Sandbox Code Playgroud)

t(*)()在两个单元中采用相同的值(类型),因此用作类型的唯一标识符T.

该值是函数的地址type<T>,所以问题是一个独特的功能,type<T>程序被保证一个定义规则.iso 3.2/3说

每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义.

其中3.2/2

一个非重载函数,其名称显示为可能被评估的表达式或[...],使用的是odr,除非[...]

我假设一个函数是非内联的,如果它的地址被采用(虽然我在标准中找不到).

iso 3.2/5列出了许多例外,但对函数的唯一引用是

带有外部链接的内联函数,[...],非静态函数模板,类模板的成员函数,或者未指定某些模板参数的模板特化[...]

似乎没有一个在这里的情况.

一个可验证的示例将需要多个文件.事实上,DieterLücking给出了一个声称失败的例子,虽然它在我的情况下并没有失败(我没有采取任何形式的"保证").

那么,这是否有效?

c++ function-pointers one-definition-rule function-templates c++11

8
推荐指数
1
解决办法
382
查看次数

在x86-64 SysV ABI中,高位参数和返回值寄存器是否允许垃圾?

除了其他方面,x86-64 SysV ABI指定了如何在寄存器中传递函数参数(第一个参数in rdi,then rsi等等),以及如何传回整数返回值(in rax和then rdx表示非常大的值).

然而,我找不到的是当传递小于64位的类型时,参数或返回值寄存器的高位应该是什么.

例如,对于以下功能:

void foo(unsigned x, unsigned y);
Run Code Online (Sandbox Code Playgroud)

... x将被传入rdiyrsi,但他们只是32位.不要的高32位rdirsi必须为零?直观地说,我会假设是,但是所有gcc,clang和icc 生成代码mov在开始时都有特定的指令将高位清零,所以看起来编译器假定不然.

类似地,编译器似乎假设rax如果返回值小于64 位,则返回值的高位可能具有垃圾位.例如,以下代码中的循环:

unsigned gives32();
unsigned short gives16();

long sum32_64() {
  long total = 0;
  for (int i=1000; i--; ) {
    total += gives32();
  }
  return total;
}

long sum16_64() {
  long total = 0;
  for (int i=1000; i--; ) {
    total += …
Run Code Online (Sandbox Code Playgroud)

linux x86 x86-64 calling-convention

8
推荐指数
1
解决办法
403
查看次数