When doing source code analysis, one of the things that we tend to spend a lot of time talking about is if a particular API (i.e. function) is vulnerable or not (note that (whenever possible) I am a big believer of having working exploits for each unique "vulnerability pattern").
But, when you look at the code, the reality is that as soon as a function has a capability (i.e. it is able to do something), most likely that function is going to be vulnerable to a particular type of attack or exploit.
In most cases these vulnerabilities will be considered 'exploitable' when the function/application allows an remote attacker to do things that he is not supposed to do; manipulating HTML data or SQL queries, changing the behavior of the application, accessing non-authorized areas.
I quite like the picture where we visialize the application as a series of functions that have vulnerabilities on them and the exercise is to see if any of those functions connect to the outside world.
A good example is a data-layer function that receives as string an SQL statement to execute (which btw most of the dotnet API's allow!). Note how that function is vulnerable by design to SQL injection! But the question is, can the attacker put a payload on it?
I think that one of the good exercises to carry out, is to find out where the 'layers' of vulnerability are and start mapping them upwards (i.e via the functions that consume it), until you identify (or not) the problems.
Even when you can't identify 'exploitable' problems, you probably will be able to identify when they were very close, or cases where they were one step away from creating a vulnerability.
Of course, that depending on the language (and C++ can be more problematic than .Net), you might want to raise those 'not really exploitable today' issues with a 'to fix asap' rating. (you should also map systemic problems versus one-off problems).
From a secure design point of view, ideally, you want to see APIs built in a way that they don't expose vulnerabilities to the outside world. These API will wrap their internal vulnerabilities in a way that they actually are not vulnerable (i.e. even if the data consumed is maliciously controlled, it is not possible to exploit them)
For example, this is what (since .NET 1.4) Microsoft does with Code Access Security (CAS). They treat the CAS world (i.e. the partial trust world boundaries) as the attack surface.