Cpp Master

Cpp Master

Tech
April 3, 2018
Donny Donny 𝄡.

Tags in blue are handcrafted tags; Tags in green are generated using AutoTag.

以下皆为在开发我的 C++ 库 donnylib 的时候遇到的问题。 (一天遇到这么多 weird problems 也是很走运了)

donnylib : https://github.com/Donny-Hikari/donnylib

Weird Template Subclass

如下的程序会导致无法自动推断模板类型。(编译环境: g++ 5.4.0, -std=c++17)


template<class T>
struct FOO
{
    struct NOT
    {
        NOT() { }
    };

    FOO() { sizeof(T); }
};

template<class T>
typename FOO<T>::NOT foo(typename FOO<T>::NOT a)
{
    return a;
}

void test1()
{
    FOO<int>::NOT f;
    foo(f); // template argument deduction/substitution failed.
}

加上下面的代码也是不行的。


template<class T>
using NOT = typename FOO<T>::NOT;

template<class T>
NOT<T> foo(NOT<T> a)
{ return a; }

template<>
NOT<int> foo<int>(NOT<int> a);

void test1()
{
    NOT<int> f;
    foo(f);
}

只要是模板类的子类就会导致这样的问题。但是实际上还是可以推断出来的。当然手动特化还是可以的。不是模板类的子类也可以正常推断。

这其实是在做自己的库 donnylib 的时候遇到的问题。(不做库一般也不会有这样的问题。)最后是把子类提出来单独做模板类。

不知道标准协会什么时候会修复这个BUG. :-D 查了一下,其实这个问题多年前就有人提出来了。不过确实没有好的解决方法。只能把子类提出来放在外面,可是这样就会损失子类优势,比如交叉调用或访问。(虽然可以降低耦合度)

fwprintf 和 fwrite 调用冲突

猜猜下面代码的输出?


void test3()
{
    fwprintf(stdout, L"fwprintf: Hello\n");
    fwrite("fwrite: Hello\n", sizeof(char), 14, stdout);
    putc('?', stdout);
}

正常都以为会是:


fwprintf: Hello
fwrite: Hello
?

实际上是


$ make build run
g++ main.cpp -o ./main -std=c++17
./main
fwprintf: Hello

一旦调用了 fwprintf 后面就没法用 fwrite, putc 等输出了。

调换顺序会怎么样呢?


void test3()
{
    fwrite("fwrite: Hello\n", sizeof(char), 14, stdout);
    putc('?', stdout);
    fwprintf(stdout, L"fwprintf: Hello\n");
}

输出为:


$ make build run
g++ main.cpp -o ./main -std=c++17
./main
fwrite: Hello
?

fwprintf 也无法输出了。

用 fflush 也不行。

编译环境是 Ubuntu 16.04 4.4.0-116-generic, g++ 5.4.0 -std=c++17.

之后再详细调查一下。

在 Window 8.1, Visual Studio 2015 下测试,一切正常。

看来还有可能是系统调用的锅。

又试了下,发现不同文件不相互影响。推测是标志,缓冲区的问题。

逗号表达式的调度顺序和返回值


int f0() { printf("f0\n"); return 0; }
int f1() { printf("f1\n"); return 1; }
int fun1() { return f0(), f1(); }
int fun2() { return (f0(), f1()); }

void test2()
{
    int a, b;

    a = f0(), f1();
    donny::logstdout << "a = f0(), f1() : " << a << donny::endl;

    b = (f0(), f1());
    donny::logstdout << "b = (f0(), f1()) : " << b << donny::endl;

    donny::logstdout << "fun1 return f0(), f1() : " << fun1() << donny::endl;

    donny::logstdout << "fun2 return (f0(), f1()) : " << fun2() << donny::endl;
}

output :


$ make build run
g++ main.cpp -o ./main -std=c++17
./main
f0
f1
[Tue Apr 03 07:27:37 2018]a = f0(), f1() : 0
f0
f1
[Tue Apr 03 07:27:37 2018]b = (f0(), f1()) : 1
f0
f1
[Tue Apr 03 07:27:37 2018]fun1 return f0(), f1() : 1
f0
f1
[Tue Apr 03 07:27:37 2018]fun2 return (f0(), f1()) : 1

a = f0(), f1() 其实是 (a = f0()), f1()

其他的都是 (f0(), f1())

逗号表达式的结果是最后一个(最右)的值。